[project @ 2004-08-13 13:04:50 by simonmar]
[ghc-hetmet.git] / ghc / compiler / cmm / MachOp.hs
diff --git a/ghc/compiler/cmm/MachOp.hs b/ghc/compiler/cmm/MachOp.hs
new file mode 100644 (file)
index 0000000..55aaa3e
--- /dev/null
@@ -0,0 +1,632 @@
+-----------------------------------------------------------------------------
+--
+-- (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,
+       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"
+#include "../includes/ghcconfig.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
+
+-- ----------------------------------------------------------------------------
+-- 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]