summaryrefslogtreecommitdiff
path: root/compiler/cmm/PprC.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/cmm/PprC.hs')
-rw-r--r--compiler/cmm/PprC.hs1028
1 files changed, 1028 insertions, 0 deletions
diff --git a/compiler/cmm/PprC.hs b/compiler/cmm/PprC.hs
new file mode 100644
index 0000000000..a8d30668b7
--- /dev/null
+++ b/compiler/cmm/PprC.hs
@@ -0,0 +1,1028 @@
+-----------------------------------------------------------------------------
+--
+-- Pretty-printing of Cmm as C, suitable for feeding gcc
+--
+-- (c) The University of Glasgow 2004
+--
+-----------------------------------------------------------------------------
+
+--
+-- Print Cmm as real C, for -fvia-C
+--
+-- This is simpler than the old PprAbsC, because Cmm is "macro-expanded"
+-- relative to the old AbstractC, and many oddities/decorations have
+-- disappeared from the data type.
+--
+
+-- ToDo: save/restore volatile registers around calls.
+
+module PprC (
+ writeCs,
+ pprStringInCStyle
+ ) where
+
+#include "HsVersions.h"
+
+-- Cmm stuff
+import Cmm
+import CLabel
+import MachOp
+import ForeignCall
+
+-- Utils
+import DynFlags ( DynFlags, DynFlag(..), dopt )
+import Unique ( getUnique )
+import UniqSet
+import FiniteMap
+import UniqFM ( eltsUFM )
+import FastString
+import Outputable
+import Constants
+import StaticFlags ( opt_Unregisterised )
+
+-- The rest
+import Data.List ( intersperse, groupBy )
+import Data.Bits ( shiftR )
+import Char ( ord, chr )
+import IO ( Handle )
+import DATA_BITS
+import Data.Word ( Word8 )
+
+#ifdef DEBUG
+import PprCmm () -- instances only
+-- import Debug.Trace
+#endif
+
+#if __GLASGOW_HASKELL__ >= 504
+import Data.Array.ST
+#endif
+import MONAD_ST
+
+-- --------------------------------------------------------------------------
+-- Top level
+
+pprCs :: DynFlags -> [Cmm] -> SDoc
+pprCs dflags cmms
+ = pprCode CStyle (vcat $ map (\c -> split_marker $$ pprC c) cmms)
+ where
+ split_marker
+ | dopt Opt_SplitObjs dflags = ptext SLIT("__STG_SPLIT_MARKER")
+ | otherwise = empty
+
+writeCs :: DynFlags -> Handle -> [Cmm] -> IO ()
+writeCs dflags handle cmms
+ = printForC handle (pprCs dflags cmms)
+
+-- --------------------------------------------------------------------------
+-- Now do some real work
+--
+-- for fun, we could call cmmToCmm over the tops...
+--
+
+pprC :: Cmm -> SDoc
+pprC (Cmm tops) = vcat $ intersperse (text "") $ map pprTop tops
+
+--
+-- top level procs
+--
+pprTop :: CmmTop -> SDoc
+pprTop (CmmProc info clbl _params blocks) =
+ (if not (null info)
+ then pprDataExterns info $$
+ pprWordArray (entryLblToInfoLbl clbl) info
+ else empty) $$
+ (case blocks of
+ [] -> empty
+ -- the first block doesn't get a label:
+ (BasicBlock _ stmts : rest) -> vcat [
+ text "",
+ extern_decls,
+ (if (externallyVisibleCLabel clbl)
+ then mkFN_ else mkIF_) (pprCLabel clbl) <+> lbrace,
+ nest 8 temp_decls,
+ nest 8 mkFB_,
+ nest 8 (vcat (map pprStmt stmts)) $$
+ vcat (map pprBBlock rest),
+ nest 8 mkFE_,
+ rbrace ]
+ )
+ where
+ (temp_decls, extern_decls) = pprTempAndExternDecls blocks
+
+
+-- Chunks of static data.
+
+-- We only handle (a) arrays of word-sized things and (b) strings.
+
+pprTop (CmmData _section _ds@[CmmDataLabel lbl, CmmString str]) =
+ hcat [
+ pprLocalness lbl, ptext SLIT("char "), pprCLabel lbl,
+ ptext SLIT("[] = "), pprStringInCStyle str, semi
+ ]
+
+pprTop (CmmData _section _ds@[CmmDataLabel lbl, CmmUninitialised size]) =
+ hcat [
+ pprLocalness lbl, ptext SLIT("char "), pprCLabel lbl,
+ brackets (int size), semi
+ ]
+
+pprTop top@(CmmData _section (CmmDataLabel lbl : lits)) =
+ pprDataExterns lits $$
+ pprWordArray lbl lits
+
+-- these shouldn't appear?
+pprTop (CmmData _ _) = panic "PprC.pprTop: can't handle this data"
+
+
+-- --------------------------------------------------------------------------
+-- BasicBlocks are self-contained entities: they always end in a jump.
+--
+-- Like nativeGen/AsmCodeGen, we could probably reorder blocks to turn
+-- as many jumps as possible into fall throughs.
+--
+
+pprBBlock :: CmmBasicBlock -> SDoc
+pprBBlock (BasicBlock lbl stmts) =
+ if null stmts then
+ pprTrace "pprC.pprBBlock: curious empty code block for"
+ (pprBlockId lbl) empty
+ else
+ nest 4 (pprBlockId lbl <> colon) $$
+ nest 8 (vcat (map pprStmt stmts))
+
+-- --------------------------------------------------------------------------
+-- Info tables. Just arrays of words.
+-- See codeGen/ClosureInfo, and nativeGen/PprMach
+
+pprWordArray :: CLabel -> [CmmStatic] -> SDoc
+pprWordArray lbl ds
+ = hcat [ pprLocalness lbl, ptext SLIT("StgWord")
+ , space, pprCLabel lbl, ptext SLIT("[] = {") ]
+ $$ nest 8 (commafy (pprStatics ds))
+ $$ ptext SLIT("};")
+
+--
+-- has to be static, if it isn't globally visible
+--
+pprLocalness :: CLabel -> SDoc
+pprLocalness lbl | not $ externallyVisibleCLabel lbl = ptext SLIT("static ")
+ | otherwise = empty
+
+-- --------------------------------------------------------------------------
+-- Statements.
+--
+
+pprStmt :: CmmStmt -> SDoc
+
+pprStmt stmt = case stmt of
+ CmmNop -> empty
+ CmmComment s -> (hang (ptext SLIT("/*")) 3 (ftext s)) $$ ptext SLIT("*/")
+
+ CmmAssign dest src -> pprAssign dest src
+
+ CmmStore dest src
+ | rep == I64 && wordRep /= I64
+ -> ptext SLIT("ASSIGN_Word64") <>
+ parens (mkP_ <> pprExpr1 dest <> comma <> pprExpr src) <> semi
+
+ | rep == F64 && wordRep /= I64
+ -> ptext SLIT("ASSIGN_DBL") <>
+ parens (mkP_ <> pprExpr1 dest <> comma <> pprExpr src) <> semi
+
+ | otherwise
+ -> hsep [ pprExpr (CmmLoad dest rep), equals, pprExpr src <> semi ]
+ where
+ rep = cmmExprRep src
+
+ CmmCall (CmmForeignCall fn cconv) results args volatile ->
+ -- Controversial: leave this out for now.
+ -- pprUndef fn $$
+
+ pprCall ppr_fn cconv results args volatile
+ where
+ ppr_fn = case fn of
+ CmmLit (CmmLabel lbl) -> pprCLabel lbl
+ _other -> parens (cCast (pprCFunType cconv results args) fn)
+ -- for a dynamic call, cast the expression to
+ -- a function of the right type (we hope).
+
+ -- we #undef a function before calling it: the FFI is supposed to be
+ -- an interface specifically to C, not to C+CPP. For one thing, this
+ -- makes the via-C route more compatible with the NCG. If macros
+ -- are being used for optimisation, then inline functions are probably
+ -- better anyway.
+ pprUndef (CmmLit (CmmLabel lbl)) =
+ ptext SLIT("#undef") <+> pprCLabel lbl
+ pprUndef _ = empty
+
+ CmmCall (CmmPrim op) results args volatile ->
+ pprCall ppr_fn CCallConv results args volatile
+ where
+ ppr_fn = pprCallishMachOp_for_C op
+
+ CmmBranch ident -> pprBranch ident
+ CmmCondBranch expr ident -> pprCondBranch expr ident
+ CmmJump lbl _params -> mkJMP_(pprExpr lbl) <> semi
+ CmmSwitch arg ids -> pprSwitch arg ids
+
+pprCFunType :: CCallConv -> [(CmmReg,MachHint)] -> [(CmmExpr,MachHint)] -> SDoc
+pprCFunType cconv ress args
+ = hcat [
+ res_type ress,
+ parens (text (ccallConvAttribute cconv) <> char '*'),
+ parens (commafy (map arg_type args))
+ ]
+ where
+ res_type [] = ptext SLIT("void")
+ res_type [(one,hint)] = machRepHintCType (cmmRegRep one) hint
+
+ arg_type (expr,hint) = machRepHintCType (cmmExprRep expr) hint
+
+-- ---------------------------------------------------------------------
+-- unconditional branches
+pprBranch :: BlockId -> SDoc
+pprBranch ident = ptext SLIT("goto") <+> pprBlockId ident <> semi
+
+
+-- ---------------------------------------------------------------------
+-- conditional branches to local labels
+pprCondBranch :: CmmExpr -> BlockId -> SDoc
+pprCondBranch expr ident
+ = hsep [ ptext SLIT("if") , parens(pprExpr expr) ,
+ ptext SLIT("goto") , (pprBlockId ident) <> semi ]
+
+
+-- ---------------------------------------------------------------------
+-- a local table branch
+--
+-- we find the fall-through cases
+--
+-- N.B. we remove Nothing's from the list of branches, as they are
+-- 'undefined'. However, they may be defined one day, so we better
+-- document this behaviour.
+--
+pprSwitch :: CmmExpr -> [ Maybe BlockId ] -> SDoc
+pprSwitch e maybe_ids
+ = let pairs = [ (ix, ident) | (ix,Just ident) <- zip [0..] maybe_ids ]
+ pairs2 = [ (map fst as, snd (head as)) | as <- groupBy sndEq pairs ]
+ in
+ (hang (ptext SLIT("switch") <+> parens ( pprExpr e ) <+> lbrace)
+ 4 (vcat ( map caseify pairs2 )))
+ $$ rbrace
+
+ where
+ sndEq (_,x) (_,y) = x == y
+
+ -- fall through case
+ caseify (ix:ixs, ident) = vcat (map do_fallthrough ixs) $$ final_branch ix
+ where
+ do_fallthrough ix =
+ hsep [ ptext SLIT("case") , pprHexVal ix wordRep <> colon ,
+ ptext SLIT("/* fall through */") ]
+
+ final_branch ix =
+ hsep [ ptext SLIT("case") , pprHexVal ix wordRep <> colon ,
+ ptext SLIT("goto") , (pprBlockId ident) <> semi ]
+
+-- ---------------------------------------------------------------------
+-- Expressions.
+--
+
+-- C Types: the invariant is that the C expression generated by
+--
+-- pprExpr e
+--
+-- has a type in C which is also given by
+--
+-- machRepCType (cmmExprRep e)
+--
+-- (similar invariants apply to the rest of the pretty printer).
+
+pprExpr :: CmmExpr -> SDoc
+pprExpr e = case e of
+ CmmLit lit -> pprLit lit
+
+ CmmLoad e I64 | wordRep /= I64
+ -> ptext SLIT("PK_Word64") <> parens (mkP_ <> pprExpr1 e)
+
+ CmmLoad e F64 | wordRep /= I64
+ -> ptext SLIT("PK_DBL") <> parens (mkP_ <> pprExpr1 e)
+
+ CmmLoad (CmmReg r) rep
+ | isPtrReg r && rep == wordRep
+ -> char '*' <> pprAsPtrReg r
+
+ CmmLoad (CmmRegOff r 0) rep
+ | isPtrReg r && rep == wordRep
+ -> char '*' <> pprAsPtrReg r
+
+ CmmLoad (CmmRegOff r off) rep
+ | isPtrReg r && rep == wordRep
+ -- ToDo: check that the offset is a word multiple?
+ -> pprAsPtrReg r <> brackets (ppr (off `shiftR` wordShift))
+
+ CmmLoad expr rep ->
+ -- the general case:
+ char '*' <> parens (cCast (machRepPtrCType rep) expr)
+
+ CmmReg reg -> pprCastReg reg
+ CmmRegOff reg 0 -> pprCastReg reg
+
+ CmmRegOff reg i
+ | i > 0 -> pprRegOff (char '+') i
+ | otherwise -> pprRegOff (char '-') (-i)
+ where
+ pprRegOff op i' = pprCastReg reg <> op <> int i'
+
+ CmmMachOp mop args -> pprMachOpApp mop args
+
+pprExpr1 :: CmmExpr -> SDoc
+pprExpr1 (CmmLit lit) = pprLit1 lit
+pprExpr1 e@(CmmReg _reg) = pprExpr e
+pprExpr1 other = parens (pprExpr other)
+
+-- --------------------------------------------------------------------------
+-- MachOp applications
+
+pprMachOpApp :: MachOp -> [CmmExpr] -> SDoc
+
+pprMachOpApp op args
+ | isMulMayOfloOp op
+ = ptext SLIT("mulIntMayOflo") <> parens (commafy (map pprExpr args))
+ where isMulMayOfloOp (MO_U_MulMayOflo _) = True
+ isMulMayOfloOp (MO_S_MulMayOflo _) = True
+ isMulMayOfloOp _ = False
+
+pprMachOpApp mop args
+ = case args of
+ -- dyadic
+ [x,y] -> pprArg x <+> pprMachOp_for_C mop <+> pprArg y
+
+ -- unary
+ [x] -> pprMachOp_for_C mop <> parens (pprArg x)
+
+ _ -> panic "PprC.pprMachOp : machop with wrong number of args"
+
+ where
+ pprArg e | signedOp mop = cCast (machRepSignedCType (cmmExprRep e)) e
+ | otherwise = pprExpr1 e
+
+-- --------------------------------------------------------------------------
+-- Literals
+
+pprLit :: CmmLit -> SDoc
+pprLit lit = case lit of
+ CmmInt i rep -> pprHexVal i rep
+ CmmFloat f rep -> parens (machRepCType rep) <> (rational f)
+ CmmLabel clbl -> mkW_ <> pprCLabelAddr clbl
+ CmmLabelOff clbl i -> mkW_ <> pprCLabelAddr clbl <> char '+' <> int i
+ CmmLabelDiffOff clbl1 clbl2 i
+ -- WARNING:
+ -- * the lit must occur in the info table clbl2
+ -- * clbl1 must be an SRT, a slow entry point or a large bitmap
+ -- The Mangler is expected to convert any reference to an SRT,
+ -- a slow entry point or a large bitmap
+ -- from an info table to an offset.
+ -> mkW_ <> pprCLabelAddr clbl1 <> char '+' <> int i
+
+pprCLabelAddr lbl = char '&' <> pprCLabel lbl
+
+pprLit1 :: CmmLit -> SDoc
+pprLit1 lit@(CmmLabelOff _ _) = parens (pprLit lit)
+pprLit1 lit@(CmmLabelDiffOff _ _ _) = parens (pprLit lit)
+pprLit1 lit@(CmmFloat _ _) = parens (pprLit lit)
+pprLit1 other = pprLit other
+
+-- ---------------------------------------------------------------------------
+-- Static data
+
+pprStatics :: [CmmStatic] -> [SDoc]
+pprStatics [] = []
+pprStatics (CmmStaticLit (CmmFloat f F32) : rest)
+ = pprLit1 (floatToWord f) : pprStatics rest
+pprStatics (CmmStaticLit (CmmFloat f F64) : rest)
+ = map pprLit1 (doubleToWords f) ++ pprStatics rest
+pprStatics (CmmStaticLit (CmmInt i I64) : rest)
+ | machRepByteWidth I32 == wORD_SIZE
+#ifdef WORDS_BIGENDIAN
+ = pprStatics (CmmStaticLit (CmmInt q I32) :
+ CmmStaticLit (CmmInt r I32) : rest)
+#else
+ = pprStatics (CmmStaticLit (CmmInt r I32) :
+ CmmStaticLit (CmmInt q I32) : rest)
+#endif
+ where r = i .&. 0xffffffff
+ q = i `shiftR` 32
+pprStatics (CmmStaticLit lit : rest)
+ = pprLit1 lit : pprStatics rest
+pprStatics (other : rest)
+ = pprPanic "pprWord" (pprStatic other)
+
+pprStatic :: CmmStatic -> SDoc
+pprStatic s = case s of
+
+ CmmStaticLit lit -> nest 4 (pprLit lit)
+ CmmAlign i -> nest 4 (ptext SLIT("/* align */") <+> int i)
+ CmmDataLabel clbl -> pprCLabel clbl <> colon
+ CmmUninitialised i -> nest 4 (mkC_ <> brackets (int i))
+
+ -- these should be inlined, like the old .hc
+ CmmString s' -> nest 4 (mkW_ <> parens(pprStringInCStyle s'))
+
+
+-- ---------------------------------------------------------------------------
+-- Block Ids
+
+pprBlockId :: BlockId -> SDoc
+pprBlockId b = char '_' <> ppr (getUnique b)
+
+-- --------------------------------------------------------------------------
+-- Print a MachOp in a way suitable for emitting via C.
+--
+
+pprMachOp_for_C :: MachOp -> SDoc
+
+pprMachOp_for_C mop = case mop of
+
+ -- Integer operations
+ MO_Add _ -> char '+'
+ MO_Sub _ -> char '-'
+ MO_Eq _ -> ptext SLIT("==")
+ MO_Ne _ -> ptext SLIT("!=")
+ MO_Mul _ -> char '*'
+
+ MO_S_Quot _ -> char '/'
+ MO_S_Rem _ -> char '%'
+ MO_S_Neg _ -> char '-'
+
+ MO_U_Quot _ -> char '/'
+ MO_U_Rem _ -> char '%'
+
+ -- Signed comparisons (floating-point comparisons also use these)
+ -- & Unsigned comparisons
+ MO_S_Ge _ -> ptext SLIT(">=")
+ MO_S_Le _ -> ptext SLIT("<=")
+ MO_S_Gt _ -> char '>'
+ MO_S_Lt _ -> char '<'
+
+ MO_U_Ge _ -> ptext SLIT(">=")
+ MO_U_Le _ -> ptext SLIT("<=")
+ MO_U_Gt _ -> char '>'
+ MO_U_Lt _ -> char '<'
+
+ -- Bitwise operations. Not all of these may be supported at all
+ -- sizes, and only integral MachReps are valid.
+ MO_And _ -> char '&'
+ MO_Or _ -> char '|'
+ MO_Xor _ -> char '^'
+ MO_Not _ -> char '~'
+ MO_Shl _ -> ptext SLIT("<<")
+ MO_U_Shr _ -> ptext SLIT(">>") -- unsigned shift right
+ MO_S_Shr _ -> ptext SLIT(">>") -- signed shift right
+
+-- Conversions. Some of these will be NOPs.
+-- Floating-point conversions use the signed variant.
+-- We won't know to generate (void*) casts here, but maybe from
+-- context elsewhere
+
+-- noop casts
+ MO_U_Conv I8 I8 -> empty
+ MO_U_Conv I16 I16 -> empty
+ MO_U_Conv I32 I32 -> empty
+ MO_U_Conv I64 I64 -> empty
+ MO_U_Conv I128 I128 -> empty
+ MO_S_Conv I8 I8 -> empty
+ MO_S_Conv I16 I16 -> empty
+ MO_S_Conv I32 I32 -> empty
+ MO_S_Conv I64 I64 -> empty
+ MO_S_Conv I128 I128 -> empty
+
+ MO_U_Conv _from to -> parens (machRepCType to)
+ MO_S_Conv _from to -> parens (machRepSignedCType to)
+
+ _ -> panic "PprC.pprMachOp_for_C: unknown machop"
+
+signedOp :: MachOp -> Bool
+signedOp (MO_S_Quot _) = True
+signedOp (MO_S_Rem _) = True
+signedOp (MO_S_Neg _) = True
+signedOp (MO_S_Ge _) = True
+signedOp (MO_S_Le _) = True
+signedOp (MO_S_Gt _) = True
+signedOp (MO_S_Lt _) = True
+signedOp (MO_S_Shr _) = True
+signedOp (MO_S_Conv _ _) = True
+signedOp _ = False
+
+-- ---------------------------------------------------------------------
+-- tend to be implemented by foreign calls
+
+pprCallishMachOp_for_C :: CallishMachOp -> SDoc
+
+pprCallishMachOp_for_C mop
+ = case mop of
+ MO_F64_Pwr -> ptext SLIT("pow")
+ MO_F64_Sin -> ptext SLIT("sin")
+ MO_F64_Cos -> ptext SLIT("cos")
+ MO_F64_Tan -> ptext SLIT("tan")
+ MO_F64_Sinh -> ptext SLIT("sinh")
+ MO_F64_Cosh -> ptext SLIT("cosh")
+ MO_F64_Tanh -> ptext SLIT("tanh")
+ MO_F64_Asin -> ptext SLIT("asin")
+ MO_F64_Acos -> ptext SLIT("acos")
+ MO_F64_Atan -> ptext SLIT("atan")
+ MO_F64_Log -> ptext SLIT("log")
+ MO_F64_Exp -> ptext SLIT("exp")
+ MO_F64_Sqrt -> ptext SLIT("sqrt")
+ MO_F32_Pwr -> ptext SLIT("powf")
+ MO_F32_Sin -> ptext SLIT("sinf")
+ MO_F32_Cos -> ptext SLIT("cosf")
+ MO_F32_Tan -> ptext SLIT("tanf")
+ MO_F32_Sinh -> ptext SLIT("sinhf")
+ MO_F32_Cosh -> ptext SLIT("coshf")
+ MO_F32_Tanh -> ptext SLIT("tanhf")
+ MO_F32_Asin -> ptext SLIT("asinf")
+ MO_F32_Acos -> ptext SLIT("acosf")
+ MO_F32_Atan -> ptext SLIT("atanf")
+ MO_F32_Log -> ptext SLIT("logf")
+ MO_F32_Exp -> ptext SLIT("expf")
+ MO_F32_Sqrt -> ptext SLIT("sqrtf")
+
+-- ---------------------------------------------------------------------
+-- Useful #defines
+--
+
+mkJMP_, mkFN_, mkIF_ :: SDoc -> SDoc
+
+mkJMP_ i = ptext SLIT("JMP_") <> parens i
+mkFN_ i = ptext SLIT("FN_") <> parens i -- externally visible function
+mkIF_ i = ptext SLIT("IF_") <> parens i -- locally visible
+
+
+mkFB_, mkFE_ :: SDoc
+mkFB_ = ptext SLIT("FB_") -- function code begin
+mkFE_ = ptext SLIT("FE_") -- function code end
+
+-- from includes/Stg.h
+--
+mkC_,mkW_,mkP_,mkPP_,mkI_,mkA_,mkD_,mkF_,mkB_,mkL_,mkLI_,mkLW_ :: SDoc
+
+mkC_ = ptext SLIT("(C_)") -- StgChar
+mkW_ = ptext SLIT("(W_)") -- StgWord
+mkP_ = ptext SLIT("(P_)") -- StgWord*
+mkPP_ = ptext SLIT("(PP_)") -- P_*
+mkI_ = ptext SLIT("(I_)") -- StgInt
+mkA_ = ptext SLIT("(A_)") -- StgAddr
+mkD_ = ptext SLIT("(D_)") -- const StgWord*
+mkF_ = ptext SLIT("(F_)") -- StgFunPtr
+mkB_ = ptext SLIT("(B_)") -- StgByteArray
+mkL_ = ptext SLIT("(L_)") -- StgClosurePtr
+
+mkLI_ = ptext SLIT("(LI_)") -- StgInt64
+mkLW_ = ptext SLIT("(LW_)") -- StgWord64
+
+
+-- ---------------------------------------------------------------------
+--
+-- Assignments
+--
+-- Generating assignments is what we're all about, here
+--
+pprAssign :: CmmReg -> CmmExpr -> SDoc
+
+-- dest is a reg, rhs is a reg
+pprAssign r1 (CmmReg r2)
+ | not (isStrangeTypeReg r1) && not (isStrangeTypeReg r2)
+ || isPtrReg r1 && isPtrReg r2
+ = hcat [ pprAsPtrReg r1, equals, pprAsPtrReg r2, semi ]
+
+-- dest is a reg, rhs is a CmmRegOff
+pprAssign r1 (CmmRegOff r2 off)
+ | not (isStrangeTypeReg r1) && not (isStrangeTypeReg r2)
+ || isPtrReg r1 && isPtrReg r2
+ = hcat [ pprAsPtrReg r1, equals, pprAsPtrReg r2, op, int off', semi ]
+ where
+ off1 | isPtrReg r2 = off `shiftR` wordShift
+ | otherwise = off
+
+ (op,off') | off >= 0 = (char '+', off1)
+ | otherwise = (char '-', -off1)
+
+-- dest is a reg, rhs is anything.
+-- We can't cast the lvalue, so we have to cast the rhs if necessary. Casting
+-- the lvalue elicits a warning from new GCC versions (3.4+).
+pprAssign r1 r2
+ | isPtrReg r1
+ = pprAsPtrReg r1 <> ptext SLIT(" = ") <> mkP_ <> pprExpr1 r2 <> semi
+ | Just ty <- strangeRegType r1
+ = pprReg r1 <> ptext SLIT(" = ") <> parens ty <> pprExpr1 r2 <> semi
+ | otherwise
+ = pprReg r1 <> ptext SLIT(" = ") <> pprExpr r2 <> semi
+
+-- ---------------------------------------------------------------------
+-- Registers
+
+pprCastReg reg
+ | isStrangeTypeReg reg = mkW_ <> pprReg reg
+ | otherwise = pprReg reg
+
+-- True if the register has type StgPtr in C, otherwise it has an
+-- integer type. We need to take care with pointer arithmetic on registers
+-- with type StgPtr.
+isPtrReg :: CmmReg -> Bool
+isPtrReg (CmmLocal _) = False
+isPtrReg (CmmGlobal r) = isPtrGlobalReg r
+
+isPtrGlobalReg :: GlobalReg -> Bool
+isPtrGlobalReg (VanillaReg n) = True
+isPtrGlobalReg Sp = True
+isPtrGlobalReg Hp = True
+isPtrGlobalReg HpLim = True
+isPtrGlobalReg SpLim = True
+isPtrGlobalReg _ = False
+
+-- True if in C this register doesn't have the type given by
+-- (machRepCType (cmmRegRep reg)), so it has to be cast.
+isStrangeTypeReg :: CmmReg -> Bool
+isStrangeTypeReg (CmmLocal _) = False
+isStrangeTypeReg (CmmGlobal g) = isStrangeTypeGlobal g
+
+isStrangeTypeGlobal :: GlobalReg -> Bool
+isStrangeTypeGlobal CurrentTSO = True
+isStrangeTypeGlobal CurrentNursery = True
+isStrangeTypeGlobal BaseReg = True
+isStrangeTypeGlobal r = isPtrGlobalReg r
+
+strangeRegType :: CmmReg -> Maybe SDoc
+strangeRegType (CmmGlobal CurrentTSO) = Just (ptext SLIT("struct StgTSO_ *"))
+strangeRegType (CmmGlobal CurrentNursery) = Just (ptext SLIT("struct bdescr_ *"))
+strangeRegType (CmmGlobal BaseReg) = Just (ptext SLIT("struct StgRegTable_ *"))
+strangeRegType _ = Nothing
+
+-- pprReg just prints the register name.
+--
+pprReg :: CmmReg -> SDoc
+pprReg r = case r of
+ CmmLocal local -> pprLocalReg local
+ CmmGlobal global -> pprGlobalReg global
+
+pprAsPtrReg :: CmmReg -> SDoc
+pprAsPtrReg (CmmGlobal (VanillaReg n)) = char 'R' <> int n <> ptext SLIT(".p")
+pprAsPtrReg other_reg = pprReg other_reg
+
+pprGlobalReg :: GlobalReg -> SDoc
+pprGlobalReg gr = case gr of
+ VanillaReg n -> char 'R' <> int n <> ptext SLIT(".w")
+ FloatReg n -> char 'F' <> int n
+ DoubleReg n -> char 'D' <> int n
+ LongReg n -> char 'L' <> int n
+ Sp -> ptext SLIT("Sp")
+ SpLim -> ptext SLIT("SpLim")
+ Hp -> ptext SLIT("Hp")
+ HpLim -> ptext SLIT("HpLim")
+ CurrentTSO -> ptext SLIT("CurrentTSO")
+ CurrentNursery -> ptext SLIT("CurrentNursery")
+ HpAlloc -> ptext SLIT("HpAlloc")
+ BaseReg -> ptext SLIT("BaseReg")
+ GCEnter1 -> ptext SLIT("stg_gc_enter_1")
+ GCFun -> ptext SLIT("stg_gc_fun")
+
+pprLocalReg :: LocalReg -> SDoc
+pprLocalReg (LocalReg uniq _rep) = char '_' <> ppr uniq
+
+-- -----------------------------------------------------------------------------
+-- Foreign Calls
+
+pprCall :: SDoc -> CCallConv -> [(CmmReg,MachHint)] -> [(CmmExpr,MachHint)]
+ -> Maybe [GlobalReg] -> SDoc
+
+pprCall ppr_fn cconv results args vols
+ | not (is_cish cconv)
+ = panic "pprCall: unknown calling convention"
+
+ | otherwise
+ = save vols $$
+ ptext SLIT("CALLER_SAVE_SYSTEM") $$
+#if x86_64_TARGET_ARCH
+ -- HACK around gcc optimisations.
+ -- x86_64 needs a __DISCARD__() here, to create a barrier between
+ -- putting the arguments into temporaries and passing the arguments
+ -- to the callee, because the argument expressions may refer to
+ -- machine registers that are also used for passing arguments in the
+ -- C calling convention.
+ (if (not opt_Unregisterised)
+ then ptext SLIT("__DISCARD__();")
+ else empty) $$
+#endif
+ ppr_assign results (ppr_fn <> parens (commafy (map pprArg args))) <> semi $$
+ ptext SLIT("CALLER_RESTORE_SYSTEM") $$
+ restore vols
+ where
+ ppr_assign [] rhs = rhs
+ ppr_assign [(reg@(CmmGlobal BaseReg), hint)] rhs
+ | Just ty <- strangeRegType reg
+ = ptext SLIT("ASSIGN_BaseReg") <> parens (parens ty <> rhs)
+ -- BaseReg is special, sometimes it isn't an lvalue and we
+ -- can't assign to it.
+ ppr_assign [(one,hint)] rhs
+ | Just ty <- strangeRegType one
+ = pprReg one <> ptext SLIT(" = ") <> parens ty <> rhs
+ | otherwise
+ = pprReg one <> ptext SLIT(" = ")
+ <> pprUnHint hint (cmmRegRep one) <> rhs
+ ppr_assign _other _rhs = panic "pprCall: multiple results"
+
+ pprArg (expr, PtrHint)
+ = cCast (ptext SLIT("void *")) expr
+ -- see comment by machRepHintCType below
+ pprArg (expr, SignedHint)
+ = cCast (machRepSignedCType (cmmExprRep expr)) expr
+ pprArg (expr, _other)
+ = pprExpr expr
+
+ pprUnHint PtrHint rep = parens (machRepCType rep)
+ pprUnHint SignedHint rep = parens (machRepCType rep)
+ pprUnHint _ _ = empty
+
+ save = save_restore SLIT("CALLER_SAVE")
+ restore = save_restore SLIT("CALLER_RESTORE")
+
+ -- Nothing says "I don't know what's live; save everything"
+ -- CALLER_SAVE_USER is defined in ghc/includes/Regs.h
+ save_restore txt Nothing = ptext txt <> ptext SLIT("_USER")
+ save_restore txt (Just these) = vcat (map saveRestoreGlobal these)
+ where saveRestoreGlobal r = ptext txt <> char '_' <> pprGlobalRegName r
+
+pprGlobalRegName :: GlobalReg -> SDoc
+pprGlobalRegName gr = case gr of
+ VanillaReg n -> char 'R' <> int n -- without the .w suffix
+ _ -> pprGlobalReg gr
+
+-- Currently we only have these two calling conventions, but this might
+-- change in the future...
+is_cish CCallConv = True
+is_cish StdCallConv = True
+
+-- ---------------------------------------------------------------------
+-- Find and print local and external declarations for a list of
+-- Cmm statements.
+--
+pprTempAndExternDecls :: [CmmBasicBlock] -> (SDoc{-temps-}, SDoc{-externs-})
+pprTempAndExternDecls stmts
+ = (vcat (map pprTempDecl (eltsUFM temps)),
+ vcat (map (pprExternDecl False{-ToDo-}) (keysFM lbls)))
+ where (temps, lbls) = runTE (mapM_ te_BB stmts)
+
+pprDataExterns :: [CmmStatic] -> SDoc
+pprDataExterns statics
+ = vcat (map (pprExternDecl False{-ToDo-}) (keysFM lbls))
+ where (_, lbls) = runTE (mapM_ te_Static statics)
+
+pprTempDecl :: LocalReg -> SDoc
+pprTempDecl l@(LocalReg _uniq rep)
+ = hcat [ machRepCType rep, space, pprLocalReg l, semi ]
+
+pprExternDecl :: Bool -> CLabel -> SDoc
+pprExternDecl in_srt lbl
+ -- do not print anything for "known external" things
+ | not (needsCDecl lbl) = empty
+ | otherwise =
+ hcat [ visibility, label_type (labelType lbl),
+ lparen, dyn_wrapper (pprCLabel lbl), text ");" ]
+ where
+ dyn_wrapper d
+ | in_srt && labelDynamic lbl = text "DLL_IMPORT_DATA_VAR" <> parens d
+ | otherwise = d
+
+ label_type CodeLabel = ptext SLIT("F_")
+ label_type DataLabel = ptext SLIT("I_")
+
+ visibility
+ | externallyVisibleCLabel lbl = char 'E'
+ | otherwise = char 'I'
+
+
+type TEState = (UniqSet LocalReg, FiniteMap CLabel ())
+newtype TE a = TE { unTE :: TEState -> (a, TEState) }
+
+instance Monad TE where
+ TE m >>= k = TE $ \s -> case m s of (a, s') -> unTE (k a) s'
+ return a = TE $ \s -> (a, s)
+
+te_lbl :: CLabel -> TE ()
+te_lbl lbl = TE $ \(temps,lbls) -> ((), (temps, addToFM lbls lbl ()))
+
+te_temp :: LocalReg -> TE ()
+te_temp r = TE $ \(temps,lbls) -> ((), (addOneToUniqSet temps r, lbls))
+
+runTE :: TE () -> TEState
+runTE (TE m) = snd (m (emptyUniqSet, emptyFM))
+
+te_Static :: CmmStatic -> TE ()
+te_Static (CmmStaticLit lit) = te_Lit lit
+te_Static _ = return ()
+
+te_BB :: CmmBasicBlock -> TE ()
+te_BB (BasicBlock _ ss) = mapM_ te_Stmt ss
+
+te_Lit :: CmmLit -> TE ()
+te_Lit (CmmLabel l) = te_lbl l
+te_Lit (CmmLabelOff l _) = te_lbl l
+te_Lit (CmmLabelDiffOff l1 l2 _) = te_lbl l1
+te_Lit _ = return ()
+
+te_Stmt :: CmmStmt -> TE ()
+te_Stmt (CmmAssign r e) = te_Reg r >> te_Expr e
+te_Stmt (CmmStore l r) = te_Expr l >> te_Expr r
+te_Stmt (CmmCall _ rs es _) = mapM_ (te_Reg.fst) rs >>
+ mapM_ (te_Expr.fst) es
+te_Stmt (CmmCondBranch e _) = te_Expr e
+te_Stmt (CmmSwitch e _) = te_Expr e
+te_Stmt (CmmJump e _) = te_Expr e
+te_Stmt _ = return ()
+
+te_Expr :: CmmExpr -> TE ()
+te_Expr (CmmLit lit) = te_Lit lit
+te_Expr (CmmLoad e _) = te_Expr e
+te_Expr (CmmReg r) = te_Reg r
+te_Expr (CmmMachOp _ es) = mapM_ te_Expr es
+te_Expr (CmmRegOff r _) = te_Reg r
+
+te_Reg :: CmmReg -> TE ()
+te_Reg (CmmLocal l) = te_temp l
+te_Reg _ = return ()
+
+
+-- ---------------------------------------------------------------------
+-- C types for MachReps
+
+cCast :: SDoc -> CmmExpr -> SDoc
+cCast ty expr = parens ty <> pprExpr1 expr
+
+-- This is for finding the types of foreign call arguments. For a pointer
+-- argument, we always cast the argument to (void *), to avoid warnings from
+-- the C compiler.
+machRepHintCType :: MachRep -> MachHint -> SDoc
+machRepHintCType rep PtrHint = ptext SLIT("void *")
+machRepHintCType rep SignedHint = machRepSignedCType rep
+machRepHintCType rep _other = machRepCType rep
+
+machRepPtrCType :: MachRep -> SDoc
+machRepPtrCType r | r == wordRep = ptext SLIT("P_")
+ | otherwise = machRepCType r <> char '*'
+
+machRepCType :: MachRep -> SDoc
+machRepCType r | r == wordRep = ptext SLIT("W_")
+ | otherwise = sized_type
+ where sized_type = case r of
+ I8 -> ptext SLIT("StgWord8")
+ I16 -> ptext SLIT("StgWord16")
+ I32 -> ptext SLIT("StgWord32")
+ I64 -> ptext SLIT("StgWord64")
+ F32 -> ptext SLIT("StgFloat") -- ToDo: correct?
+ F64 -> ptext SLIT("StgDouble")
+ _ -> panic "machRepCType"
+
+machRepSignedCType :: MachRep -> SDoc
+machRepSignedCType r | r == wordRep = ptext SLIT("I_")
+ | otherwise = sized_type
+ where sized_type = case r of
+ I8 -> ptext SLIT("StgInt8")
+ I16 -> ptext SLIT("StgInt16")
+ I32 -> ptext SLIT("StgInt32")
+ I64 -> ptext SLIT("StgInt64")
+ F32 -> ptext SLIT("StgFloat") -- ToDo: correct?
+ F64 -> ptext SLIT("StgDouble")
+ _ -> panic "machRepCType"
+
+-- ---------------------------------------------------------------------
+-- print strings as valid C strings
+
+pprStringInCStyle :: [Word8] -> SDoc
+pprStringInCStyle s = doubleQuotes (text (concatMap charToC s))
+
+charToC :: Word8 -> String
+charToC w =
+ case chr (fromIntegral w) of
+ '\"' -> "\\\""
+ '\'' -> "\\\'"
+ '\\' -> "\\\\"
+ c | c >= ' ' && c <= '~' -> [c]
+ | otherwise -> ['\\',
+ chr (ord '0' + ord c `div` 64),
+ chr (ord '0' + ord c `div` 8 `mod` 8),
+ chr (ord '0' + ord c `mod` 8)]
+
+-- ---------------------------------------------------------------------------
+-- Initialising static objects with floating-point numbers. We can't
+-- just emit the floating point number, because C will cast it to an int
+-- by rounding it. We want the actual bit-representation of the float.
+
+-- This is a hack to turn the floating point numbers into ints that we
+-- can safely initialise to static locations.
+
+big_doubles
+ | machRepByteWidth F64 == 2 * wORD_SIZE = True
+ | machRepByteWidth F64 == wORD_SIZE = False
+ | otherwise = panic "big_doubles"
+
+#if __GLASGOW_HASKELL__ >= 504
+newFloatArray :: (Int,Int) -> ST s (STUArray s Int Float)
+newFloatArray = newArray_
+
+newDoubleArray :: (Int,Int) -> ST s (STUArray s Int Double)
+newDoubleArray = newArray_
+
+castFloatToIntArray :: STUArray s Int Float -> ST s (STUArray s Int Int)
+castFloatToIntArray = castSTUArray
+
+castDoubleToIntArray :: STUArray s Int Double -> ST s (STUArray s Int Int)
+castDoubleToIntArray = castSTUArray
+
+writeFloatArray :: STUArray s Int Float -> Int -> Float -> ST s ()
+writeFloatArray = writeArray
+
+writeDoubleArray :: STUArray s Int Double -> Int -> Double -> ST s ()
+writeDoubleArray = writeArray
+
+readIntArray :: STUArray s Int Int -> Int -> ST s Int
+readIntArray = readArray
+
+#else
+
+castFloatToIntArray :: MutableByteArray s t -> ST s (MutableByteArray s t)
+castFloatToIntArray = return
+
+castDoubleToIntArray :: MutableByteArray s t -> ST s (MutableByteArray s t)
+castDoubleToIntArray = return
+
+#endif
+
+-- floats are always 1 word
+floatToWord :: Rational -> CmmLit
+floatToWord r
+ = runST (do
+ arr <- newFloatArray ((0::Int),0)
+ writeFloatArray arr 0 (fromRational r)
+ arr' <- castFloatToIntArray arr
+ i <- readIntArray arr' 0
+ return (CmmInt (toInteger i) wordRep)
+ )
+
+doubleToWords :: Rational -> [CmmLit]
+doubleToWords r
+ | big_doubles -- doubles are 2 words
+ = runST (do
+ arr <- newDoubleArray ((0::Int),1)
+ writeDoubleArray arr 0 (fromRational r)
+ arr' <- castDoubleToIntArray arr
+ i1 <- readIntArray arr' 0
+ i2 <- readIntArray arr' 1
+ return [ CmmInt (toInteger i1) wordRep
+ , CmmInt (toInteger i2) wordRep
+ ]
+ )
+ | otherwise -- doubles are 1 word
+ = runST (do
+ arr <- newDoubleArray ((0::Int),0)
+ writeDoubleArray arr 0 (fromRational r)
+ arr' <- castDoubleToIntArray arr
+ i <- readIntArray arr' 0
+ return [ CmmInt (toInteger i) wordRep ]
+ )
+
+-- ---------------------------------------------------------------------------
+-- Utils
+
+wordShift :: Int
+wordShift = machRepLogWidth wordRep
+
+commafy :: [SDoc] -> SDoc
+commafy xs = hsep $ punctuate comma xs
+
+-- Print in C hex format: 0x13fa
+pprHexVal :: Integer -> MachRep -> SDoc
+pprHexVal 0 _ = ptext SLIT("0x0")
+pprHexVal w rep
+ | w < 0 = parens (char '-' <> ptext SLIT("0x") <> go (-w) <> repsuffix rep)
+ | otherwise = ptext SLIT("0x") <> go w <> repsuffix rep
+ where
+ -- type suffix for literals:
+ -- Integer literals are unsigned in Cmm/C. We explicitly cast to
+ -- signed values for doing signed operations, but at all other
+ -- times values are unsigned. This also helps eliminate occasional
+ -- warnings about integer overflow from gcc.
+
+ -- on 32-bit platforms, add "ULL" to 64-bit literals
+ repsuffix I64 | wORD_SIZE == 4 = ptext SLIT("ULL")
+ -- on 64-bit platforms with 32-bit int, add "L" to 64-bit literals
+ repsuffix I64 | cINT_SIZE == 4 = ptext SLIT("UL")
+ repsuffix _ = char 'U'
+
+ go 0 = empty
+ go w' = go q <> dig
+ where
+ (q,r) = w' `quotRem` 16
+ dig | r < 10 = char (chr (fromInteger r + ord '0'))
+ | otherwise = char (chr (fromInteger r - 10 + ord 'a'))
+