summaryrefslogtreecommitdiff
path: root/compiler/cmm/MachOp.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/cmm/MachOp.hs')
-rw-r--r--compiler/cmm/MachOp.hs652
1 files changed, 652 insertions, 0 deletions
diff --git a/compiler/cmm/MachOp.hs b/compiler/cmm/MachOp.hs
new file mode 100644
index 0000000000..5bbff6de78
--- /dev/null
+++ b/compiler/cmm/MachOp.hs
@@ -0,0 +1,652 @@
+-----------------------------------------------------------------------------
+--
+-- (c) The University of Glasgow 2002-2004
+--
+-- 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
+
+-- -----------------------------------------------------------------------------
+-- 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
+ 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]