X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Fcmm%2FMachOp.hs;fp=compiler%2Fcmm%2FMachOp.hs;h=5bbff6de7837491bebc9803dc5805605aae035e4;hb=0065d5ab628975892cea1ec7303f968c3338cbe1;hp=0000000000000000000000000000000000000000;hpb=28a464a75e14cece5db40f2765a29348273ff2d2;p=ghc-hetmet.git diff --git a/compiler/cmm/MachOp.hs b/compiler/cmm/MachOp.hs new file mode 100644 index 0000000..5bbff6d --- /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]