-----------------------------------------------------------------------------
module Cmm (
- GenCmm(..), Cmm, RawCmm,
- GenCmmTop(..), CmmTop, RawCmmTop,
- CmmInfo(..), ClosureTypeInfo(..), ProfilingInfo(..), ClosureTypeTag,
- GenBasicBlock(..), CmmBasicBlock, blockId, blockStmts,
- CmmStmt(..), CmmActuals, CmmFormal, CmmFormals, CmmHintFormals,
+ GenCmm(..), Cmm, RawCmm,
+ GenCmmTop(..), CmmTop, RawCmmTop,
+ ListGraph(..),
+ cmmMapGraph, cmmTopMapGraph,
+ cmmMapGraphM, cmmTopMapGraphM,
+ CmmInfo(..), UpdateFrame(..),
+ CmmInfoTable(..), HasStaticClosure, ClosureTypeInfo(..), ConstrDescription,
+ ProfilingInfo(..), ClosureTypeTag,
+ GenBasicBlock(..), CmmBasicBlock, blockId, blockStmts, mapBlockStmts,
+ CmmReturnInfo(..),
+ CmmStmt(..), CmmActual, CmmActuals, CmmFormal, CmmFormals,
+ HintedCmmFormal, HintedCmmFormals, HintedCmmActual, HintedCmmActuals,
CmmSafety(..),
- CmmCallTarget(..),
- CmmStatic(..), Section(..),
- CmmExpr(..), cmmExprRep,
- CmmReg(..), cmmRegRep,
- CmmLit(..), cmmLitRep,
- LocalReg(..), localRegRep, localRegGCFollow, Kind(..),
- BlockId(..), BlockEnv,
- GlobalReg(..), globalRegRep,
-
- node, nodeReg, spReg, hpReg, spLimReg
+ CmmCallTarget(..), CallishMachOp(..), pprCallishMachOp,
+ ForeignHint(..), CmmHinted(..),
+ CmmStatic(..), Section(..),
+ module CmmExpr,
) where
#include "HsVersions.h"
-import MachOp
+import BlockId
+import CmmExpr
import CLabel
import ForeignCall
import SMRep
+
import ClosureInfo
-import Unique
-import UniqFM
+import Outputable
import FastString
import Data.Word
+
+-- A [[BlockId]] is a local label.
+-- Local labels must be unique within an entire compilation unit, not
+-- just a single top-level item, because local labels map one-to-one
+-- with assembly-language labels.
+
-----------------------------------------------------------------------------
--- Cmm, CmmTop, CmmBasicBlock
+-- Cmm, CmmTop, CmmBasicBlock
-----------------------------------------------------------------------------
-- A file is a list of top-level chunks. These may be arbitrarily
-- re-orderd during code generation.
-- GenCmm is abstracted over
--- (a) the type of static data elements
--- (b) the contents of a basic block.
+-- d, the type of static data elements in CmmData
+-- h, the static info preceding the code of a CmmProc
+-- g, the control-flow graph of a CmmProc
+--
-- We expect there to be two main instances of this type:
--- (a) Plain C--, i.e. populated with CmmLit and CmmExpr respectively,
--- (b) Native code, populated with instructions
+-- (a) C--, i.e. populated with various C-- constructs
+-- (Cmm and RawCmm below)
+-- (b) Native code, populated with data/instructions
--
-newtype GenCmm d h i = Cmm [GenCmmTop d h i]
-
--- | Cmm with the info table as a data type
-type Cmm = GenCmm CmmStatic CmmInfo CmmStmt
-
--- | Cmm with the info tables converted to a list of 'CmmStatic'
-type RawCmm = GenCmm CmmStatic [CmmStatic] CmmStmt
+-- A second family of instances based on ZipCfg is work in progress.
+--
+newtype GenCmm d h g = Cmm [GenCmmTop d h g]
--- A top-level chunk, abstracted over the type of the contents of
+-- | A top-level chunk, abstracted over the type of the contents of
-- the basic blocks (Cmm or instructions are the likely instantiations).
-data GenCmmTop d h i
- = CmmProc
+data GenCmmTop d h g
+ = CmmProc -- A procedure
h -- Extra header such as the info table
CLabel -- Used to generate both info & entry labels
- CmmFormals -- Argument locals live on entry (C-- procedure params)
- [GenBasicBlock i] -- Code, may be empty. The first block is
- -- the entry point. The order is otherwise initially
- -- unimportant, but at some point the code gen will
- -- fix the order.
+ CmmFormals -- Argument locals live on entry (C-- procedure params)
+ -- XXX Odd that there are no kinds, but there you are ---NR
+ g -- Control-flow graph for the procedure's code
+
+ | CmmData -- Static data
+ Section
+ [d]
+
+-- | A control-flow graph represented as a list of extended basic blocks.
+newtype ListGraph i = ListGraph [GenBasicBlock i]
+ -- ^ Code, may be empty. The first block is the entry point. The
+ -- order is otherwise initially unimportant, but at some point the
+ -- code gen will fix the order.
+
+ -- BlockIds must be unique across an entire compilation unit, since
+ -- they are translated to assembly-language labels, which scope
+ -- across a whole compilation unit.
- -- the BlockId of the first block does not give rise
- -- to a label. To jump to the first block in a Proc,
- -- use the appropriate CLabel.
+-- | Cmm with the info table as a data type
+type Cmm = GenCmm CmmStatic CmmInfo (ListGraph CmmStmt)
+type CmmTop = GenCmmTop CmmStatic CmmInfo (ListGraph CmmStmt)
- -- some static data.
- | CmmData Section [d] -- constant values only
+-- | Cmm with the info tables converted to a list of 'CmmStatic'
+type RawCmm = GenCmm CmmStatic [CmmStatic] (ListGraph CmmStmt)
+type RawCmmTop = GenCmmTop CmmStatic [CmmStatic] (ListGraph CmmStmt)
-type CmmTop = GenCmmTop CmmStatic CmmInfo CmmStmt
-type RawCmmTop = GenCmmTop CmmStatic [CmmStatic] CmmStmt
-- A basic block containing a single label, at the beginning.
-- The list of basic blocks in a top-level code block may be re-ordered.
-- blocks in order to turn some jumps into fallthroughs.
data GenBasicBlock i = BasicBlock BlockId [i]
- -- ToDo: Julian suggests that we might need to annotate this type
- -- with the out & in edges in the graph, i.e. two * [BlockId]. This
- -- information can be derived from the contents, but it might be
- -- helpful to cache it here.
+type CmmBasicBlock = GenBasicBlock CmmStmt
-type CmmBasicBlock = GenBasicBlock CmmStmt
+instance UserOfLocalRegs i => UserOfLocalRegs (GenBasicBlock i) where
+ foldRegsUsed f set (BasicBlock _ l) = foldRegsUsed f set l
blockId :: GenBasicBlock i -> BlockId
-- The branch block id is that of the first block in
blockStmts :: GenBasicBlock i -> [i]
blockStmts (BasicBlock _ stmts) = stmts
+
+mapBlockStmts :: (i -> i') -> GenBasicBlock i -> GenBasicBlock i'
+mapBlockStmts f (BasicBlock id bs) = BasicBlock id (map f bs)
+----------------------------------------------------------------
+-- graph maps
+----------------------------------------------------------------
+
+cmmMapGraph :: (g -> g') -> GenCmm d h g -> GenCmm d h g'
+cmmTopMapGraph :: (g -> g') -> GenCmmTop d h g -> GenCmmTop d h g'
+
+cmmMapGraphM :: Monad m => (String -> g -> m g') -> GenCmm d h g -> m (GenCmm d h g')
+cmmTopMapGraphM :: Monad m => (String -> g -> m g') -> GenCmmTop d h g -> m (GenCmmTop d h g')
+
+cmmMapGraph f (Cmm tops) = Cmm $ map (cmmTopMapGraph f) tops
+cmmTopMapGraph f (CmmProc h l args g) = CmmProc h l args (f g)
+cmmTopMapGraph _ (CmmData s ds) = CmmData s ds
+
+cmmMapGraphM f (Cmm tops) = mapM (cmmTopMapGraphM f) tops >>= return . Cmm
+cmmTopMapGraphM f (CmmProc h l args g) =
+ f (showSDoc $ ppr l) g >>= return . CmmProc h l args
+cmmTopMapGraphM _ (CmmData s ds) = return $ CmmData s ds
+
-----------------------------------------------------------------------------
-- Info Tables
-----------------------------------------------------------------------------
--- Info table as a haskell data type
data CmmInfo
= CmmInfo
+ (Maybe BlockId) -- GC target. Nothing <=> CPS won't do stack check
+ -- JD: NOT USED BY NEW CODE GEN
+ (Maybe UpdateFrame) -- Update frame
+ CmmInfoTable -- Info table
+
+-- Info table as a haskell data type
+data CmmInfoTable
+ = CmmInfoTable
+ HasStaticClosure
ProfilingInfo
- (Maybe BlockId) -- GC target
ClosureTypeTag -- Int
ClosureTypeInfo
- | CmmNonInfo -- Procedure doesn't need an info table
- (Maybe BlockId) -- But we still need a GC target for it
+ | CmmNonInfoTable -- Procedure doesn't need an info table
+
+type HasStaticClosure = Bool
-- TODO: The GC target shouldn't really be part of CmmInfo
-- as it doesn't appear in the resulting info table.
data ClosureTypeInfo
= ConstrInfo ClosureLayout ConstrTag ConstrDescription
- | FunInfo ClosureLayout C_SRT FunType FunArity ArgDescr SlowEntry
- | ThunkInfo ClosureLayout C_SRT
+ | FunInfo ClosureLayout C_SRT FunArity ArgDescr SlowEntry
+ | ThunkInfo ClosureLayout C_SRT
| ThunkSelectorInfo SelectorOffset C_SRT
| ContInfo
- [Maybe LocalReg] -- Forced stack parameters
+ [Maybe LocalReg] -- Stack layout: Just x, an item x
+ -- Nothing: a 1-word gap
+ -- Start of list is the *young* end
C_SRT
+data CmmReturnInfo = CmmMayReturn
+ | CmmNeverReturns
+ deriving ( Eq )
+
-- TODO: These types may need refinement
data ProfilingInfo = ProfilingInfo CmmLit CmmLit -- closure_type, closure_desc
type ClosureTypeTag = StgHalfWord
type ClosureLayout = (StgHalfWord, StgHalfWord) -- ptrs, nptrs
type ConstrTag = StgHalfWord
type ConstrDescription = CmmLit
-type FunType = StgHalfWord
type FunArity = StgHalfWord
type SlowEntry = CmmLit
- -- ^We would like this to be a CLabel but
+ -- We would like this to be a CLabel but
-- for now the parser sets this to zero on an INFO_TABLE_FUN.
type SelectorOffset = StgWord
+-- | A frame that is to be pushed before entry to the function.
+-- Used to handle 'update' frames.
+data UpdateFrame =
+ UpdateFrame
+ CmmExpr -- Frame header. Behaves like the target of a 'jump'.
+ [CmmExpr] -- Frame remainder. Behaves like the arguments of a 'jump'.
+
-----------------------------------------------------------------------------
-- CmmStmt
-- A "statement". Note that all branches are explicit: there are no
-- control to a new function.
-----------------------------------------------------------------------------
-data CmmStmt
+data CmmStmt -- Old-style
= CmmNop
| CmmComment FastString
| CmmAssign CmmReg CmmExpr -- Assign to register
| CmmStore CmmExpr CmmExpr -- Assign to memory location. Size is
- -- given by cmmExprRep of the rhs.
+ -- given by cmmExprType of the rhs.
- | CmmCall -- A foreign call, with
+ | CmmCall -- A call (forign, native or primitive), with
CmmCallTarget
- CmmHintFormals -- zero or more results
- CmmActuals -- zero or more arguments
+ HintedCmmFormals -- zero or more results
+ HintedCmmActuals -- zero or more arguments
CmmSafety -- whether to build a continuation
+ CmmReturnInfo
| CmmBranch BlockId -- branch to another BB in this fn
-- one -> second block etc
-- Undefined outside range, and when there's a Nothing
- | CmmJump CmmExpr -- Jump to another function,
- CmmActuals -- with these parameters.
+ | CmmJump CmmExpr -- Jump to another C-- function,
+ HintedCmmActuals -- with these parameters. (parameters never used)
- | CmmReturn -- Return from a function,
- CmmActuals -- with these return values.
+ | CmmReturn -- Return from a native C-- function,
+ HintedCmmActuals -- with these return values. (parameters never used)
type CmmActual = CmmExpr
-type CmmActuals = [(CmmActual,MachHint)]
type CmmFormal = LocalReg
-type CmmHintFormals = [(CmmFormal,MachHint)]
+type CmmActuals = [CmmActual]
type CmmFormals = [CmmFormal]
-data CmmSafety = CmmUnsafe | CmmSafe C_SRT
+
+data CmmHinted a = CmmHinted { hintlessCmm :: a, cmmHint :: ForeignHint }
+ deriving( Eq )
+
+type HintedCmmActuals = [HintedCmmActual]
+type HintedCmmFormals = [HintedCmmFormal]
+type HintedCmmFormal = CmmHinted CmmFormal
+type HintedCmmActual = CmmHinted CmmActual
+
+data CmmSafety = CmmUnsafe | CmmSafe C_SRT
+
+-- | enable us to fold used registers over 'CmmActuals' and 'CmmFormals'
+instance UserOfLocalRegs CmmStmt where
+ foldRegsUsed f (set::b) s = stmt s set
+ where
+ stmt :: CmmStmt -> b -> b
+ stmt (CmmNop) = id
+ stmt (CmmComment {}) = id
+ stmt (CmmAssign _ e) = gen e
+ stmt (CmmStore e1 e2) = gen e1 . gen e2
+ stmt (CmmCall target _ es _ _) = gen target . gen es
+ stmt (CmmBranch _) = id
+ stmt (CmmCondBranch e _) = gen e
+ stmt (CmmSwitch e _) = gen e
+ stmt (CmmJump e es) = gen e . gen es
+ stmt (CmmReturn es) = gen es
+
+ gen :: UserOfLocalRegs a => a -> b -> b
+ gen a set = foldRegsUsed f set a
+
+instance UserOfLocalRegs CmmCallTarget where
+ foldRegsUsed f set (CmmCallee e _) = foldRegsUsed f set e
+ foldRegsUsed _ set (CmmPrim {}) = set
+
+instance UserOfSlots CmmCallTarget where
+ foldSlotsUsed f set (CmmCallee e _) = foldSlotsUsed f set e
+ foldSlotsUsed _ set (CmmPrim {}) = set
+
+instance UserOfLocalRegs a => UserOfLocalRegs (CmmHinted a) where
+ foldRegsUsed f set a = foldRegsUsed f set (hintlessCmm a)
+
+instance UserOfSlots a => UserOfSlots (CmmHinted a) where
+ foldSlotsUsed f set a = foldSlotsUsed f set (hintlessCmm a)
+
+instance DefinerOfLocalRegs a => DefinerOfLocalRegs (CmmHinted a) where
+ foldRegsDefd f set a = foldRegsDefd f set (hintlessCmm a)
{-
Discussion
non-local conditional jump is to encode it as a branch to a block that
contains a single jump. This leads to inefficient code in the back end.
+[N.B. This problem will go away when we make the transition to the
+'zipper' form of control-flow graph, in which both targets of a
+conditional jump are explicit. ---NR]
+
One possible way to fix this would be:
data CmmStat =
-----------------------------------------------------------------------------
data CmmCallTarget
- = CmmForeignCall -- Call to a foreign function
+ = CmmCallee -- Call a function (foreign or native)
CmmExpr -- literal label <=> static call
-- other expression <=> dynamic call
CCallConv -- The calling convention
- | CmmPrim -- Call to a "primitive" (eg. sin, cos)
+ | CmmPrim -- Call a "primitive" (eg. sin, cos)
CallishMachOp -- These might be implemented as inline
-- code by the backend.
-
------------------------------------------------------------------------------
--- CmmExpr
--- An expression. Expressions have no side effects.
------------------------------------------------------------------------------
-
-data CmmExpr
- = CmmLit CmmLit -- Literal
- | CmmLoad CmmExpr MachRep -- Read memory location
- | CmmReg CmmReg -- Contents of register
- | CmmMachOp MachOp [CmmExpr] -- Machine operation (+, -, *, etc.)
- | CmmRegOff CmmReg Int
- -- CmmRegOff reg i
- -- ** is shorthand only, meaning **
- -- CmmMachOp (MO_S_Add rep (CmmReg reg) (CmmLit (CmmInt i rep)))
- -- where rep = cmmRegRep reg
deriving Eq
-cmmExprRep :: CmmExpr -> MachRep
-cmmExprRep (CmmLit lit) = cmmLitRep lit
-cmmExprRep (CmmLoad _ rep) = rep
-cmmExprRep (CmmReg reg) = cmmRegRep reg
-cmmExprRep (CmmMachOp op _) = resultRepOfMachOp op
-cmmExprRep (CmmRegOff reg _) = cmmRegRep reg
-data CmmReg
- = CmmLocal LocalReg
- | CmmGlobal GlobalReg
+data ForeignHint
+ = NoHint | AddrHint | SignedHint
deriving( Eq )
-
-cmmRegRep :: CmmReg -> MachRep
-cmmRegRep (CmmLocal reg) = localRegRep reg
-cmmRegRep (CmmGlobal reg) = globalRegRep reg
-
--- | Whether a 'LocalReg' is a GC followable pointer
-data Kind = KindPtr | KindNonPtr deriving (Eq)
-
-data LocalReg
- = LocalReg
- !Unique -- ^ Identifier
- MachRep -- ^ Type
- Kind -- ^ Should the GC follow as a pointer
-
-instance Eq LocalReg where
- (LocalReg u1 _ _) == (LocalReg u2 _ _) = u1 == u2
-
-instance Uniquable LocalReg where
- getUnique (LocalReg uniq _ _) = uniq
-
-localRegRep :: LocalReg -> MachRep
-localRegRep (LocalReg _ rep _) = rep
-
-localRegGCFollow (LocalReg _ _ p) = p
-
-data CmmLit
- = CmmInt Integer MachRep
- -- Interpretation: the 2's complement representation of the value
- -- is truncated to the specified size. This is easier than trying
- -- to keep the value within range, because we don't know whether
- -- it will be used as a signed or unsigned value (the MachRep doesn't
- -- distinguish between signed & unsigned).
- | CmmFloat Rational MachRep
- | CmmLabel CLabel -- Address of label
- | CmmLabelOff CLabel Int -- Address of label + byte offset
+ -- Used to give extra per-argument or per-result
+ -- information needed by foreign calling conventions
+
+
+-- CallishMachOps 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
+ | MO_Touch -- Keep variables live (when using interior pointers)
+ deriving (Eq, Show)
+
+pprCallishMachOp :: CallishMachOp -> SDoc
+pprCallishMachOp mo = text (show mo)
- -- Due to limitations in the C backend, the following
- -- MUST ONLY be used inside the info table indicated by label2
- -- (label2 must be the info label), and label1 must be an
- -- SRT, a slow entrypoint or a large bitmap (see the Mangler)
- -- Don't use it at all unless tablesNextToCode.
- -- It is also used inside the NCG during when generating
- -- position-independent code.
- | CmmLabelDiffOff CLabel CLabel Int -- label1 - label2 + offset
- deriving Eq
-
-cmmLitRep :: CmmLit -> MachRep
-cmmLitRep (CmmInt _ rep) = rep
-cmmLitRep (CmmFloat _ rep) = rep
-cmmLitRep (CmmLabel _) = wordRep
-cmmLitRep (CmmLabelOff _ _) = wordRep
-cmmLitRep (CmmLabelDiffOff _ _ _) = wordRep
-
------------------------------------------------------------------------------
--- A local label.
-
--- Local labels must be unique within a single compilation unit.
-
-newtype BlockId = BlockId Unique
- deriving (Eq,Ord)
-
-instance Uniquable BlockId where
- getUnique (BlockId u) = u
-
-type BlockEnv a = UniqFM {- BlockId -} a
-
-----------------------------------------------------------------------------
-- Static Data
-----------------------------------------------------------------------------
| CmmString [Word8]
-- string of 8-bit values only, not zero terminated.
------------------------------------------------------------------------------
--- Global STG registers
------------------------------------------------------------------------------
-
-data GlobalReg
- -- Argument and return registers
- = VanillaReg -- pointers, unboxed ints and chars
- {-# UNPACK #-} !Int -- its number
-
- | FloatReg -- single-precision floating-point registers
- {-# UNPACK #-} !Int -- its number
-
- | DoubleReg -- double-precision floating-point registers
- {-# UNPACK #-} !Int -- its number
-
- | LongReg -- long int registers (64-bit, really)
- {-# UNPACK #-} !Int -- its number
-
- -- STG registers
- | Sp -- Stack ptr; points to last occupied stack location.
- | SpLim -- Stack limit
- | Hp -- Heap ptr; points to last occupied heap location.
- | HpLim -- Heap limit register
- | CurrentTSO -- pointer to current thread's TSO
- | CurrentNursery -- pointer to allocation area
- | HpAlloc -- allocation count for heap check failure
-
- -- We keep the address of some commonly-called
- -- functions in the register table, to keep code
- -- size down:
- | GCEnter1 -- stg_gc_enter_1
- | GCFun -- stg_gc_fun
-
- -- Base offset for the register table, used for accessing registers
- -- which do not have real registers assigned to them. This register
- -- will only appear after we have expanded GlobalReg into memory accesses
- -- (where necessary) in the native code generator.
- | BaseReg
-
- -- Base Register for PIC (position-independent code) calculations
- -- Only used inside the native code generator. It's exact meaning differs
- -- from platform to platform (see module PositionIndependentCode).
- | PicBaseReg
-
- deriving( Eq
-#ifdef DEBUG
- , Show
-#endif
- )
-
--- convenient aliases
-spReg, hpReg, spLimReg, nodeReg :: CmmReg
-spReg = CmmGlobal Sp
-hpReg = CmmGlobal Hp
-spLimReg = CmmGlobal SpLim
-nodeReg = CmmGlobal node
-
-node :: GlobalReg
-node = VanillaReg 1
-
-globalRegRep :: GlobalReg -> MachRep
-globalRegRep (VanillaReg _) = wordRep
-globalRegRep (FloatReg _) = F32
-globalRegRep (DoubleReg _) = F64
-globalRegRep (LongReg _) = I64
-globalRegRep _ = wordRep