-{-# OPTIONS -w #-}
--- The above warning supression flag is a temporary kludge.
--- While working on this module you are encouraged to remove it and fix
--- any warnings in the module. See
--- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
--- for details
-
------------------------------------------------------------------------------
---
--- (c) The University of Glasgow 2002-2006
---
--- Low-level machine operations, used in the Cmm datatype.
---
------------------------------------------------------------------------------
-
-module MachOp (
- MachRep(..),
- machRepBitWidth,
- machRepByteWidth,
- machRepLogWidth,
- isFloatingRep,
-
- MachHint(..),
-
- MachOp(..),
- pprMachOp,
- isCommutableMachOp,
- isAssociativeMachOp,
- isComparisonMachOp,
- resultRepOfMachOp,
- machOpArgReps,
- maybeInvertComparison,
-
- CallishMachOp(..),
- pprCallishMachOp,
-
- wordRep,
- halfWordRep,
- cIntRep, cLongRep,
-
- mo_wordAdd,
- mo_wordSub,
- mo_wordEq,
- mo_wordNe,
- mo_wordMul,
- mo_wordSQuot,
- mo_wordSRem,
- mo_wordSNeg,
- mo_wordUQuot,
- mo_wordURem,
-
- mo_wordSGe,
- mo_wordSLe,
- mo_wordSGt,
- mo_wordSLt,
-
- mo_wordUGe,
- mo_wordULe,
- mo_wordUGt,
- mo_wordULt,
-
- mo_wordAnd,
- mo_wordOr,
- mo_wordXor,
- mo_wordNot,
- mo_wordShl,
- mo_wordSShr,
- mo_wordUShr,
-
- mo_u_8To32,
- mo_s_8To32,
- mo_u_16To32,
- mo_s_16To32,
-
- mo_u_8ToWord,
- mo_s_8ToWord,
- mo_u_16ToWord,
- mo_s_16ToWord,
- mo_u_32ToWord,
- mo_s_32ToWord,
-
- mo_32To8,
- mo_32To16,
- mo_WordTo8,
- mo_WordTo16,
- mo_WordTo32,
- ) where
-
-#include "HsVersions.h"
-
-import Constants
-import Outputable
-import FastString
-
--- -----------------------------------------------------------------------------
--- MachRep
-
-{- |
-A MachRep is the "representation" of a value in Cmm. It is used for
-resource allocation: eg. which kind of register a value should be
-stored in.
-
-The primary requirement is that there exists a function
-
- cmmExprRep :: CmmExpr -> MachRep
-
-This means that:
-
- - a register has an implicit MachRep
- - a literal has an implicit MachRep
- - an operation (MachOp) has an implicit result MachRep
-
-It also means that we can check that the arguments to a MachOp have
-the correct MachRep, i.e. we can do a kind of lint-style type checking
-on Cmm.
--}
-
-data MachRep
- = I8
- | I16
- | I32
- | I64
- | I128
- | F32
- | F64
- | F80 -- extended double-precision, used in x86 native codegen only.
- deriving (Eq, Ord, Show)
-
-mrStr I8 = sLit "I8"
-mrStr I16 = sLit "I16"
-mrStr I32 = sLit "I32"
-mrStr I64 = sLit "I64"
-mrStr I128 = sLit "I128"
-mrStr F32 = sLit "F32"
-mrStr F64 = sLit "F64"
-mrStr F80 = sLit "F80"
-
-instance Outputable MachRep where
- ppr rep = ptext (mrStr rep)
-
-{-
-Implementation notes:
-
-It might suffice to keep just a width, without distinguishing between
-floating and integer types. However, keeping the distinction will
-help the native code generator to assign registers more easily.
--}
-
-{-
-Should a MachRep include a signed vs. unsigned distinction?
-
-This is very much like a "hint" in C-- terminology: it isn't necessary
-in order to generate correct code, but it might be useful in that the
-compiler can generate better code if it has access to higher-level
-hints about data. This is important at call boundaries, because the
-definition of a function is not visible at all of its call sites, so
-the compiler cannot infer the hints.
-
-Here in Cmm, we're taking a slightly different approach. We include
-the int vs. float hint in the MachRep, because (a) the majority of
-platforms have a strong distinction between float and int registers,
-and (b) we don't want to do any heavyweight hint-inference in the
-native code backend in order to get good code. We're treating the
-hint more like a type: our Cmm is always completely consistent with
-respect to hints. All coercions between float and int are explicit.
-
-What about the signed vs. unsigned hint? This information might be
-useful if we want to keep sub-word-sized values in word-size
-registers, which we must do if we only have word-sized registers.
-
-On such a system, there are two straightforward conventions for
-representing sub-word-sized values:
-
-(a) Leave the upper bits undefined. Comparison operations must
- sign- or zero-extend both operands before comparing them,
- depending on whether the comparison is signed or unsigned.
-
-(b) Always keep the values sign- or zero-extended as appropriate.
- Arithmetic operations must narrow the result to the appropriate
- size.
-
-A clever compiler might not use either (a) or (b) exclusively, instead
-it would attempt to minimize the coercions by analysis: the same kind
-of analysis that propagates hints around. In Cmm we don't want to
-have to do this, so we plump for having richer types and keeping the
-type information consistent.
-
-If signed/unsigned hints are missing from MachRep, then the only
-choice we have is (a), because we don't know whether the result of an
-operation should be sign- or zero-extended.
-
-Many architectures have extending load operations, which work well
-with (b). To make use of them with (a), you need to know whether the
-value is going to be sign- or zero-extended by an enclosing comparison
-(for example), which involves knowing above the context. This is
-doable but more complex.
-
-Further complicating the issue is foreign calls: a foreign calling
-convention can specify that signed 8-bit quantities are passed as
-sign-extended 32 bit quantities, for example (this is the case on the
-PowerPC). So we *do* need sign information on foreign call arguments.
-
-Pros for adding signed vs. unsigned to MachRep:
-
- - It would let us use convention (b) above, and get easier
- code generation for extending loads.
-
- - Less information required on foreign calls.
-
- - MachOp type would be simpler
-
-Cons:
-
- - More complexity
-
- - What is the MachRep for a VanillaReg? Currently it is
- always wordRep, but now we have to decide whether it is
- signed or unsigned. The same VanillaReg can thus have
- different MachReps in different parts of the program.
-
- - Extra coercions cluttering up expressions.
-
-Currently for GHC, the foreign call point is moot, because we do our
-own promotion of sub-word-sized values to word-sized values. The Int8
-type is represnted by an Int# which is kept sign-extended at all times
-(this is slightly naughty, because we're making assumptions about the
-C calling convention rather early on in the compiler). However, given
-this, the cons outweigh the pros.
-
--}
-
-
-machRepBitWidth :: MachRep -> Int
-machRepBitWidth I8 = 8
-machRepBitWidth I16 = 16
-machRepBitWidth I32 = 32
-machRepBitWidth I64 = 64
-machRepBitWidth I128 = 128
-machRepBitWidth F32 = 32
-machRepBitWidth F64 = 64
-machRepBitWidth F80 = 80
-
-machRepByteWidth :: MachRep -> Int
-machRepByteWidth I8 = 1
-machRepByteWidth I16 = 2
-machRepByteWidth I32 = 4
-machRepByteWidth I64 = 8
-machRepByteWidth I128 = 16
-machRepByteWidth F32 = 4
-machRepByteWidth F64 = 8
-machRepByteWidth F80 = 10
-
--- log_2 of the width in bytes, useful for generating shifts.
-machRepLogWidth :: MachRep -> Int
-machRepLogWidth I8 = 0
-machRepLogWidth I16 = 1
-machRepLogWidth I32 = 2
-machRepLogWidth I64 = 3
-machRepLogWidth I128 = 4
-machRepLogWidth F32 = 2
-machRepLogWidth F64 = 3
-machRepLogWidth F80 = panic "machRepLogWidth: F80"
-
-isFloatingRep :: MachRep -> Bool
-isFloatingRep F32 = True
-isFloatingRep F64 = True
-isFloatingRep F80 = True
-isFloatingRep _ = False
-
--- -----------------------------------------------------------------------------
--- Hints
-
-{-
-A hint gives a little more information about a data value. Hints are
-used on the arguments to a foreign call, where the code generator needs
-to know some extra information on top of the MachRep of each argument in
-order to generate a correct call.
--}
-
-data MachHint
- = NoHint
- | PtrHint
- | SignedHint
- | FloatHint
- deriving Eq
-
-mhStr NoHint = sLit "NoHint"
-mhStr PtrHint = sLit "PtrHint"
-mhStr SignedHint = sLit "SignedHint"
-mhStr FloatHint = sLit "FloatHint"
-
-instance Outputable MachHint where
- ppr hint = ptext (mhStr hint)
-
--- -----------------------------------------------------------------------------
--- MachOp
-
-{- |
-Machine-level primops; ones which we can reasonably delegate to the
-native code generators to handle. Basically contains C's primops
-and no others.
-
-Nomenclature: all ops indicate width and signedness, where
-appropriate. Widths: 8\/16\/32\/64 means the given size, obviously.
-Nat means the operation works on STG word sized objects.
-Signedness: S means signed, U means unsigned. For operations where
-signedness is irrelevant or makes no difference (for example
-integer add), the signedness component is omitted.
-
-An exception: NatP is a ptr-typed native word. From the point of
-view of the native code generators this distinction is irrelevant,
-but the C code generator sometimes needs this info to emit the
-right casts.
--}
-
-data MachOp
-
- -- Integer operations
- = MO_Add MachRep
- | MO_Sub MachRep
- | MO_Eq MachRep
- | MO_Ne MachRep
- | MO_Mul MachRep -- low word of multiply
- | MO_S_MulMayOflo MachRep -- nonzero if signed multiply overflows
- | MO_S_Quot MachRep -- signed / (same semantics as IntQuotOp)
- | MO_S_Rem MachRep -- signed % (same semantics as IntRemOp)
- | MO_S_Neg MachRep -- unary -
- | MO_U_MulMayOflo MachRep -- nonzero if unsigned multiply overflows
- | MO_U_Quot MachRep -- unsigned / (same semantics as WordQuotOp)
- | MO_U_Rem MachRep -- unsigned % (same semantics as WordRemOp)
-
- -- Signed comparisons (floating-point comparisons also use these)
- | MO_S_Ge MachRep
- | MO_S_Le MachRep
- | MO_S_Gt MachRep
- | MO_S_Lt MachRep
-
- -- Unsigned comparisons
- | MO_U_Ge MachRep
- | MO_U_Le MachRep
- | MO_U_Gt MachRep
- | MO_U_Lt MachRep
-
- -- Bitwise operations. Not all of these may be supported at all sizes,
- -- and only integral MachReps are valid.
- | MO_And MachRep
- | MO_Or MachRep
- | MO_Xor MachRep
- | MO_Not MachRep
- | MO_Shl MachRep
- | MO_U_Shr MachRep -- unsigned shift right
- | MO_S_Shr MachRep -- signed shift right
-
- -- Conversions. Some of these will be NOPs.
- -- Floating-point conversions use the signed variant.
- | MO_S_Conv MachRep{-from-} MachRep{-to-} -- signed conversion
- | MO_U_Conv MachRep{-from-} MachRep{-to-} -- unsigned conversion
-
- deriving (Eq, Show)
-
-pprMachOp :: MachOp -> SDoc
-pprMachOp mo = text (show mo)
-
-
--- These MachOps tend to be implemented by foreign calls in some backends,
--- so we separate them out. In Cmm, these can only occur in a
--- statement position, in contrast to an ordinary MachOp which can occur
--- anywhere in an expression.
-data CallishMachOp
- = MO_F64_Pwr
- | MO_F64_Sin
- | MO_F64_Cos
- | MO_F64_Tan
- | MO_F64_Sinh
- | MO_F64_Cosh
- | MO_F64_Tanh
- | MO_F64_Asin
- | MO_F64_Acos
- | MO_F64_Atan
- | MO_F64_Log
- | MO_F64_Exp
- | MO_F64_Sqrt
- | MO_F32_Pwr
- | MO_F32_Sin
- | MO_F32_Cos
- | MO_F32_Tan
- | MO_F32_Sinh
- | MO_F32_Cosh
- | MO_F32_Tanh
- | MO_F32_Asin
- | MO_F32_Acos
- | MO_F32_Atan
- | MO_F32_Log
- | MO_F32_Exp
- | MO_F32_Sqrt
- | MO_WriteBarrier
- deriving (Eq, Show)
-
-pprCallishMachOp :: CallishMachOp -> SDoc
-pprCallishMachOp mo = text (show mo)
-
--- -----------------------------------------------------------------------------
--- Some common MachReps
-
--- A 'wordRep' is a machine word on the target architecture
--- Specifically, it is the size of an Int#, Word#, Addr#
--- and the unit of allocation on the stack and the heap
--- Any pointer is also guaranteed to be a wordRep.
-
-wordRep | wORD_SIZE == 4 = I32
- | wORD_SIZE == 8 = I64
- | otherwise = panic "MachOp.wordRep: Unknown word size"
-
-halfWordRep | wORD_SIZE == 4 = I16
- | wORD_SIZE == 8 = I32
- | otherwise = panic "MachOp.halfWordRep: Unknown word size"
-
-mo_wordAdd = MO_Add wordRep
-mo_wordSub = MO_Sub wordRep
-mo_wordEq = MO_Eq wordRep
-mo_wordNe = MO_Ne wordRep
-mo_wordMul = MO_Mul wordRep
-mo_wordSQuot = MO_S_Quot wordRep
-mo_wordSRem = MO_S_Rem wordRep
-mo_wordSNeg = MO_S_Neg wordRep
-mo_wordUQuot = MO_U_Quot wordRep
-mo_wordURem = MO_U_Rem wordRep
-
-mo_wordSGe = MO_S_Ge wordRep
-mo_wordSLe = MO_S_Le wordRep
-mo_wordSGt = MO_S_Gt wordRep
-mo_wordSLt = MO_S_Lt wordRep
-
-mo_wordUGe = MO_U_Ge wordRep
-mo_wordULe = MO_U_Le wordRep
-mo_wordUGt = MO_U_Gt wordRep
-mo_wordULt = MO_U_Lt wordRep
-
-mo_wordAnd = MO_And wordRep
-mo_wordOr = MO_Or wordRep
-mo_wordXor = MO_Xor wordRep
-mo_wordNot = MO_Not wordRep
-mo_wordShl = MO_Shl wordRep
-mo_wordSShr = MO_S_Shr wordRep
-mo_wordUShr = MO_U_Shr wordRep
-
-mo_u_8To32 = MO_U_Conv I8 I32
-mo_s_8To32 = MO_S_Conv I8 I32
-mo_u_16To32 = MO_U_Conv I16 I32
-mo_s_16To32 = MO_S_Conv I16 I32
-
-mo_u_8ToWord = MO_U_Conv I8 wordRep
-mo_s_8ToWord = MO_S_Conv I8 wordRep
-mo_u_16ToWord = MO_U_Conv I16 wordRep
-mo_s_16ToWord = MO_S_Conv I16 wordRep
-mo_s_32ToWord = MO_S_Conv I32 wordRep
-mo_u_32ToWord = MO_U_Conv I32 wordRep
-
-mo_WordTo8 = MO_U_Conv wordRep I8
-mo_WordTo16 = MO_U_Conv wordRep I16
-mo_WordTo32 = MO_U_Conv wordRep I32
-
-mo_32To8 = MO_U_Conv I32 I8
-mo_32To16 = MO_U_Conv I32 I16
-
--- cIntRep is the MachRep for a C-language 'int'
-#if SIZEOF_INT == 4
-cIntRep = I32
-#elif SIZEOF_INT == 8
-cIntRep = I64
-#endif
-
-#if SIZEOF_LONG == 4
-cLongRep = I32
-#elif SIZEOF_LONG == 8
-cLongRep = I64
-#endif
-
--- ----------------------------------------------------------------------------
--- isCommutableMachOp
-
-{- |
-Returns @True@ if the MachOp has commutable arguments. This is used
-in the platform-independent Cmm optimisations.
-
-If in doubt, return @False@. This generates worse code on the
-native routes, but is otherwise harmless.
--}
-isCommutableMachOp :: MachOp -> Bool
-isCommutableMachOp mop =
- case mop of
- MO_Add _ -> True
- MO_Eq _ -> True
- MO_Ne _ -> True
- MO_Mul _ -> True
- MO_S_MulMayOflo _ -> True
- MO_U_MulMayOflo _ -> True
- MO_And _ -> True
- MO_Or _ -> True
- MO_Xor _ -> True
- _other -> False
-
--- ----------------------------------------------------------------------------
--- isAssociativeMachOp
-
-{- |
-Returns @True@ if the MachOp is associative (i.e. @(x+y)+z == x+(y+z)@)
-This is used in the platform-independent Cmm optimisations.
-
-If in doubt, return @False@. This generates worse code on the
-native routes, but is otherwise harmless.
--}
-isAssociativeMachOp :: MachOp -> Bool
-isAssociativeMachOp mop =
- case mop of
- MO_Add r -> not (isFloatingRep r)
- MO_Mul r -> not (isFloatingRep r)
- MO_And _ -> True
- MO_Or _ -> True
- MO_Xor _ -> True
- _other -> False
-
--- ----------------------------------------------------------------------------
--- isComparisonMachOp
-
-{- |
-Returns @True@ if the MachOp is a comparison.
-
-If in doubt, return False. This generates worse code on the
-native routes, but is otherwise harmless.
--}
-isComparisonMachOp :: MachOp -> Bool
-isComparisonMachOp mop =
- case mop of
- MO_Eq _ -> True
- MO_Ne _ -> True
- MO_S_Ge _ -> True
- MO_S_Le _ -> True
- MO_S_Gt _ -> True
- MO_S_Lt _ -> True
- MO_U_Ge _ -> True
- MO_U_Le _ -> True
- MO_U_Gt _ -> True
- MO_U_Lt _ -> True
- _other -> False
-
--- -----------------------------------------------------------------------------
--- Inverting conditions
-
--- Sometimes it's useful to be able to invert the sense of a
--- condition. Not all conditional tests are invertible: in
--- particular, floating point conditionals cannot be inverted, because
--- there exist floating-point values which return False for both senses
--- of a condition (eg. !(NaN > NaN) && !(NaN /<= NaN)).
-
-maybeInvertComparison :: MachOp -> Maybe MachOp
-maybeInvertComparison op
- = case op of
- MO_Eq r | not (isFloatingRep r) -> Just (MO_Ne r)
- MO_Ne r | not (isFloatingRep r) -> Just (MO_Eq r)
- MO_U_Lt r | not (isFloatingRep r) -> Just (MO_U_Ge r)
- MO_U_Gt r | not (isFloatingRep r) -> Just (MO_U_Le r)
- MO_U_Le r | not (isFloatingRep r) -> Just (MO_U_Gt r)
- MO_U_Ge r | not (isFloatingRep r) -> Just (MO_U_Lt r)
- MO_S_Lt r | not (isFloatingRep r) -> Just (MO_S_Ge r)
- MO_S_Gt r | not (isFloatingRep r) -> Just (MO_S_Le r)
- MO_S_Le r | not (isFloatingRep r) -> Just (MO_S_Gt r)
- MO_S_Ge r | not (isFloatingRep r) -> Just (MO_S_Lt r)
- _other -> Nothing
-
--- ----------------------------------------------------------------------------
--- resultRepOfMachOp
-
-{- |
-Returns the MachRep of the result of a MachOp.
--}
-resultRepOfMachOp :: MachOp -> MachRep
-resultRepOfMachOp mop =
- case mop of
- MO_Add r -> r
- MO_Sub r -> r
- MO_Eq r -> comparisonResultRep
- MO_Ne r -> comparisonResultRep
- MO_Mul r -> r
- MO_S_MulMayOflo r -> r
- MO_S_Quot r -> r
- MO_S_Rem r -> r
- MO_S_Neg r -> r
- MO_U_MulMayOflo r -> r
- MO_U_Quot r -> r
- MO_U_Rem r -> r
-
- MO_S_Ge r -> comparisonResultRep
- MO_S_Le r -> comparisonResultRep
- MO_S_Gt r -> comparisonResultRep
- MO_S_Lt r -> comparisonResultRep
-
- MO_U_Ge r -> comparisonResultRep
- MO_U_Le r -> comparisonResultRep
- MO_U_Gt r -> comparisonResultRep
- MO_U_Lt r -> comparisonResultRep
-
- MO_And r -> r
- MO_Or r -> r
- MO_Xor r -> r
- MO_Not r -> r
- MO_Shl r -> r
- MO_U_Shr r -> r
- MO_S_Shr r -> r
-
- MO_S_Conv from to -> to
- MO_U_Conv from to -> to
-
-
-comparisonResultRep = wordRep -- is it?
-
-
--- -----------------------------------------------------------------------------
--- machOpArgReps
-
--- | This function is used for debugging only: we can check whether an
--- application of a MachOp is "type-correct" by checking that the MachReps of
--- its arguments are the same as the MachOp expects. This is used when
--- linting a CmmExpr.
-
-machOpArgReps :: MachOp -> [MachRep]
-machOpArgReps op =
- case op of
- MO_Add r -> [r,r]
- MO_Sub r -> [r,r]
- MO_Eq r -> [r,r]
- MO_Ne r -> [r,r]
- MO_Mul r -> [r,r]
- MO_S_MulMayOflo r -> [r,r]
- MO_S_Quot r -> [r,r]
- MO_S_Rem r -> [r,r]
- MO_S_Neg r -> [r]
- MO_U_MulMayOflo r -> [r,r]
- MO_U_Quot r -> [r,r]
- MO_U_Rem r -> [r,r]
-
- MO_S_Ge r -> [r,r]
- MO_S_Le r -> [r,r]
- MO_S_Gt r -> [r,r]
- MO_S_Lt r -> [r,r]
-
- MO_U_Ge r -> [r,r]
- MO_U_Le r -> [r,r]
- MO_U_Gt r -> [r,r]
- MO_U_Lt r -> [r,r]
-
- MO_And r -> [r,r]
- MO_Or r -> [r,r]
- MO_Xor r -> [r,r]
- MO_Not r -> [r]
- MO_Shl r -> [r,wordRep]
- MO_U_Shr r -> [r,wordRep]
- MO_S_Shr r -> [r,wordRep]
-
- MO_S_Conv from to -> [from]
- MO_U_Conv from to -> [from]