module StgCmmUtils (
cgLit, mkSimpleLit,
emitDataLits, mkDataLits,
emitRODataLits, mkRODataLits,
emitRtsCall, emitRtsCallWithVols, emitRtsCallWithResult, emitRtsCallGen,
assignTemp, newTemp, withTemp,
newUnboxedTupleRegs,
mkMultiAssign, mkCmmSwitch, mkCmmLitSwitch,
emitSwitch,
tagToClosure, mkTaggedObjectLoad,
callerSaves, callerSaveVolatileRegs, get_GlobalReg_addr,
cmmAndWord, cmmOrWord, cmmNegate, cmmEqWord, cmmNeWord,
cmmUGtWord, cmmSubWord, cmmMulWord, cmmAddWord, cmmUShrWord,
cmmOffsetExprW, cmmOffsetExprB,
cmmRegOffW, cmmRegOffB,
cmmLabelOffW, cmmLabelOffB,
cmmOffsetW, cmmOffsetB,
cmmOffsetLitW, cmmOffsetLitB,
cmmLoadIndexW,
cmmConstrTag, cmmConstrTag1,
cmmUntag, cmmIsTagged, cmmGetTag,
addToMem, addToMemE, addToMemLbl,
mkWordCLit,
newStringCLit, newByteStringCLit,
packHalfWordsCLit,
blankWord,
getSRTInfo, srt_escape
) where
#include "HsVersions.h"
#include "../includes/stg/MachRegs.h"
import StgCmmMonad
import StgCmmClosure
import Cmm
import BlockId
import MkGraph
import CLabel
import CmmUtils
import ForeignCall
import IdInfo
import Type
import TyCon
import Constants
import SMRep
import StgSyn ( SRT(..) )
import Module
import Literal
import Digraph
import ListSetOps
import Util
import Unique
import DynFlags
import FastString
import Outputable
import Data.Char
import Data.Word
import Data.Maybe
cgLit :: Literal -> FCode CmmLit
cgLit (MachStr s) = newByteStringCLit (bytesFS s)
cgLit other_lit = return (mkSimpleLit other_lit)
mkLtOp :: Literal -> MachOp
mkLtOp (MachInt _) = MO_S_Lt wordWidth
mkLtOp (MachFloat _) = MO_F_Lt W32
mkLtOp (MachDouble _) = MO_F_Lt W64
mkLtOp lit = MO_U_Lt (typeWidth (cmmLitType (mkSimpleLit lit)))
mkSimpleLit :: Literal -> CmmLit
mkSimpleLit (MachChar c) = CmmInt (fromIntegral (ord c)) wordWidth
mkSimpleLit MachNullAddr = zeroCLit
mkSimpleLit (MachInt i) = CmmInt i wordWidth
mkSimpleLit (MachInt64 i) = CmmInt i W64
mkSimpleLit (MachWord i) = CmmInt i wordWidth
mkSimpleLit (MachWord64 i) = CmmInt i W64
mkSimpleLit (MachFloat r) = CmmFloat r W32
mkSimpleLit (MachDouble r) = CmmFloat r W64
mkSimpleLit (MachLabel fs ms fod)
= CmmLabel (mkForeignLabel fs ms labelSrc fod)
where
labelSrc = ForeignLabelInThisPackage
mkSimpleLit other = pprPanic "mkSimpleLit" (ppr other)
addToMemLbl :: CmmType -> CLabel -> Int -> CmmAGraph
addToMemLbl rep lbl n = addToMem rep (CmmLit (CmmLabel lbl)) n
addToMem :: CmmType
-> CmmExpr
-> Int
-> CmmAGraph
addToMem rep ptr n = addToMemE rep ptr (CmmLit (CmmInt (toInteger n) (typeWidth rep)))
addToMemE :: CmmType
-> CmmExpr
-> CmmExpr
-> CmmAGraph
addToMemE rep ptr n
= mkStore ptr (CmmMachOp (MO_Add (typeWidth rep)) [CmmLoad ptr rep, n])
mkTaggedObjectLoad :: LocalReg -> LocalReg -> WordOff -> DynTag -> CmmAGraph
mkTaggedObjectLoad reg base offset tag
= mkAssign (CmmLocal reg)
(CmmLoad (cmmOffsetB (CmmReg (CmmLocal base))
(wORD_SIZE*offset tag))
(localRegType reg))
tagToClosure :: TyCon -> CmmExpr -> CmmExpr
tagToClosure tycon tag
= CmmLoad (cmmOffsetExprW closure_tbl tag) bWord
where closure_tbl = CmmLit (CmmLabel lbl)
lbl = mkClosureTableLabel (tyConName tycon) NoCafRefs
emitRtsCall :: PackageId -> FastString -> [(CmmExpr,ForeignHint)] -> Bool -> FCode ()
emitRtsCall pkg fun args safe = emitRtsCallGen [] pkg fun args Nothing safe
emitRtsCallWithVols :: PackageId -> FastString -> [(CmmExpr,ForeignHint)] -> [GlobalReg] -> Bool -> FCode ()
emitRtsCallWithVols pkg fun args vols safe
= emitRtsCallGen [] pkg fun args (Just vols) safe
emitRtsCallWithResult :: LocalReg -> ForeignHint -> PackageId -> FastString
-> [(CmmExpr,ForeignHint)] -> Bool -> FCode ()
emitRtsCallWithResult res hint pkg fun args safe
= emitRtsCallGen [(res,hint)] pkg fun args Nothing safe
emitRtsCallGen
:: [(LocalReg,ForeignHint)]
-> PackageId
-> FastString
-> [(CmmExpr,ForeignHint)]
-> Maybe [GlobalReg]
-> Bool
-> FCode ()
emitRtsCallGen res pkg fun args _vols safe
= do { updfr_off <- getUpdFrameOff
; emit caller_save
; emit $ call updfr_off
; emit caller_load }
where
call updfr_off =
if safe then
mkCmmCall fun_expr res' args' updfr_off
else
mkUnsafeCall (ForeignTarget fun_expr
(ForeignConvention CCallConv arg_hints res_hints)) res' args'
(args', arg_hints) = unzip args
(res', res_hints) = unzip res
(caller_save, caller_load) = callerSaveVolatileRegs
fun_expr = mkLblExpr (mkCmmCodeLabel pkg fun)
callerSaveVolatileRegs :: (CmmAGraph, CmmAGraph)
callerSaveVolatileRegs = (caller_save, caller_load)
where
caller_save = catAGraphs (map callerSaveGlobalReg regs_to_save)
caller_load = catAGraphs (map callerRestoreGlobalReg regs_to_save)
system_regs = [ Sp,SpLim,Hp,HpLim,CCCS,CurrentTSO,CurrentNursery
, BaseReg ]
regs_to_save = filter callerSaves system_regs
callerSaveGlobalReg reg
= mkStore (get_GlobalReg_addr reg) (CmmReg (CmmGlobal reg))
callerRestoreGlobalReg reg
= mkAssign (CmmGlobal reg)
(CmmLoad (get_GlobalReg_addr reg) (globalRegType reg))
get_GlobalReg_addr :: GlobalReg -> CmmExpr
get_GlobalReg_addr BaseReg = regTableOffset 0
get_GlobalReg_addr mid = get_Regtable_addr_from_offset
(globalRegType mid) (baseRegOffset mid)
regTableOffset :: Int -> CmmExpr
regTableOffset n =
CmmLit (CmmLabelOff mkMainCapabilityLabel (oFFSET_Capability_r + n))
get_Regtable_addr_from_offset :: CmmType -> Int -> CmmExpr
get_Regtable_addr_from_offset _rep offset =
#ifdef REG_Base
CmmRegOff (CmmGlobal BaseReg) offset
#else
regTableOffset offset
#endif
callerSaves :: GlobalReg -> Bool
#ifdef CALLER_SAVES_Base
callerSaves BaseReg = True
#endif
#ifdef CALLER_SAVES_R1
callerSaves (VanillaReg 1 _) = True
#endif
#ifdef CALLER_SAVES_R2
callerSaves (VanillaReg 2 _) = True
#endif
#ifdef CALLER_SAVES_R3
callerSaves (VanillaReg 3 _) = True
#endif
#ifdef CALLER_SAVES_R4
callerSaves (VanillaReg 4 _) = True
#endif
#ifdef CALLER_SAVES_R5
callerSaves (VanillaReg 5 _) = True
#endif
#ifdef CALLER_SAVES_R6
callerSaves (VanillaReg 6 _) = True
#endif
#ifdef CALLER_SAVES_R7
callerSaves (VanillaReg 7 _) = True
#endif
#ifdef CALLER_SAVES_R8
callerSaves (VanillaReg 8 _) = True
#endif
#ifdef CALLER_SAVES_R9
callerSaves (VanillaReg 9 _) = True
#endif
#ifdef CALLER_SAVES_R10
callerSaves (VanillaReg 10 _) = True
#endif
#ifdef CALLER_SAVES_F1
callerSaves (FloatReg 1) = True
#endif
#ifdef CALLER_SAVES_F2
callerSaves (FloatReg 2) = True
#endif
#ifdef CALLER_SAVES_F3
callerSaves (FloatReg 3) = True
#endif
#ifdef CALLER_SAVES_F4
callerSaves (FloatReg 4) = True
#endif
#ifdef CALLER_SAVES_D1
callerSaves (DoubleReg 1) = True
#endif
#ifdef CALLER_SAVES_D2
callerSaves (DoubleReg 2) = True
#endif
#ifdef CALLER_SAVES_L1
callerSaves (LongReg 1) = True
#endif
#ifdef CALLER_SAVES_Sp
callerSaves Sp = True
#endif
#ifdef CALLER_SAVES_SpLim
callerSaves SpLim = True
#endif
#ifdef CALLER_SAVES_Hp
callerSaves Hp = True
#endif
#ifdef CALLER_SAVES_HpLim
callerSaves HpLim = True
#endif
#ifdef CALLER_SAVES_CCCS
callerSaves CCCS = True
#endif
#ifdef CALLER_SAVES_CurrentTSO
callerSaves CurrentTSO = True
#endif
#ifdef CALLER_SAVES_CurrentNursery
callerSaves CurrentNursery = True
#endif
callerSaves _ = False
baseRegOffset :: GlobalReg -> Int
baseRegOffset Sp = oFFSET_StgRegTable_rSp
baseRegOffset SpLim = oFFSET_StgRegTable_rSpLim
baseRegOffset (LongReg 1) = oFFSET_StgRegTable_rL1
baseRegOffset Hp = oFFSET_StgRegTable_rHp
baseRegOffset HpLim = oFFSET_StgRegTable_rHpLim
baseRegOffset CCCS = oFFSET_StgRegTable_rCCCS
baseRegOffset CurrentTSO = oFFSET_StgRegTable_rCurrentTSO
baseRegOffset CurrentNursery = oFFSET_StgRegTable_rCurrentNursery
baseRegOffset HpAlloc = oFFSET_StgRegTable_rHpAlloc
baseRegOffset GCEnter1 = oFFSET_stgGCEnter1
baseRegOffset GCFun = oFFSET_stgGCFun
baseRegOffset reg = pprPanic "baseRegOffset:" (ppr reg)
emitDataLits :: CLabel -> [CmmLit] -> FCode ()
emitDataLits lbl lits = emitDecl (mkDataLits Data lbl lits)
emitRODataLits :: CLabel -> [CmmLit] -> FCode ()
emitRODataLits lbl lits = emitDecl (mkRODataLits lbl lits)
newStringCLit :: String -> FCode CmmLit
newStringCLit str = newByteStringCLit (map (fromIntegral . ord) str)
newByteStringCLit :: [Word8] -> FCode CmmLit
newByteStringCLit bytes
= do { uniq <- newUnique
; let (lit, decl) = mkByteStringCLit uniq bytes
; emitDecl decl
; return lit }
assignTemp :: CmmExpr -> FCode LocalReg
assignTemp (CmmReg (CmmLocal reg)) = return reg
assignTemp e = do { uniq <- newUnique
; let reg = LocalReg uniq (cmmExprType e)
; emit (mkAssign (CmmLocal reg) e)
; return reg }
newTemp :: CmmType -> FCode LocalReg
newTemp rep = do { uniq <- newUnique
; return (LocalReg uniq rep) }
newUnboxedTupleRegs :: Type -> FCode ([LocalReg], [ForeignHint])
newUnboxedTupleRegs res_ty
= ASSERT( isUnboxedTupleType res_ty )
do { sequel <- getSequel
; regs <- choose_regs sequel
; ASSERT( regs `equalLength` reps )
return (regs, map primRepForeignHint reps) }
where
ty_args = tyConAppArgs (repType res_ty)
reps = [ rep
| ty <- ty_args
, let rep = typePrimRep ty
, not (isVoidRep rep) ]
choose_regs (AssignTo regs _) = return regs
choose_regs _other = mapM (newTemp . primRepCmmType) reps
mkMultiAssign :: [LocalReg] -> [CmmExpr] -> CmmAGraph
type Key = Int
type Vrtx = (Key, Stmt)
type Stmt = (LocalReg, CmmExpr)
mkMultiAssign [] [] = mkNop
mkMultiAssign [reg] [rhs] = mkAssign (CmmLocal reg) rhs
mkMultiAssign regs rhss = ASSERT( equalLength regs rhss )
unscramble ([1..] `zip` (regs `zip` rhss))
unscramble :: [Vrtx] -> CmmAGraph
unscramble vertices
= catAGraphs (map do_component components)
where
edges :: [ (Vrtx, Key, [Key]) ]
edges = [ (vertex, key1, edges_from stmt1)
| vertex@(key1, stmt1) <- vertices ]
edges_from :: Stmt -> [Key]
edges_from stmt1 = [ key2 | (key2, stmt2) <- vertices,
stmt1 `mustFollow` stmt2 ]
components :: [SCC Vrtx]
components = stronglyConnCompFromEdgedVertices edges
do_component :: SCC Vrtx -> CmmAGraph
do_component (AcyclicSCC (_,stmt)) = mk_graph stmt
do_component (CyclicSCC []) = panic "do_component"
do_component (CyclicSCC [(_,stmt)]) = mk_graph stmt
do_component (CyclicSCC ((_,first_stmt) : rest))
= withUnique $ \u ->
let (to_tmp, from_tmp) = split u first_stmt
in mk_graph to_tmp
<*> unscramble rest
<*> mk_graph from_tmp
split :: Unique -> Stmt -> (Stmt, Stmt)
split uniq (reg, rhs)
= ((tmp, rhs), (reg, CmmReg (CmmLocal tmp)))
where
rep = cmmExprType rhs
tmp = LocalReg uniq rep
mk_graph :: Stmt -> CmmAGraph
mk_graph (reg, rhs) = mkAssign (CmmLocal reg) rhs
mustFollow :: Stmt -> Stmt -> Bool
(reg, _) `mustFollow` (_, rhs) = CmmLocal reg `regUsedIn` rhs
emitSwitch :: CmmExpr
-> [(ConTagZ, CmmAGraph)]
-> Maybe CmmAGraph
-> ConTagZ -> ConTagZ
-> FCode ()
emitSwitch tag_expr branches mb_deflt lo_tag hi_tag
= do { dflags <- getDynFlags
; emit (mkCmmSwitch (via_C dflags) tag_expr branches mb_deflt lo_tag hi_tag) }
where
via_C dflags | HscC <- hscTarget dflags = True
| otherwise = False
mkCmmSwitch :: Bool
-> CmmExpr
-> [(ConTagZ, CmmAGraph)]
-> Maybe CmmAGraph
-> ConTagZ -> ConTagZ
-> CmmAGraph
mkCmmSwitch _ _ [] (Just code) _ _ = code
mkCmmSwitch _ _ [(_,code)] Nothing _ _ = code
mkCmmSwitch via_C tag_expr branches mb_deflt lo_tag hi_tag
= withFreshLabel "switch join" $ \ join_lbl ->
label_default join_lbl mb_deflt $ \ mb_deflt ->
label_branches join_lbl branches $ \ branches ->
assignTemp' tag_expr $ \tag_expr' ->
mk_switch tag_expr' (sortLe le branches) mb_deflt
lo_tag hi_tag via_C
<*> mkLabel join_lbl
where
(t1,_) `le` (t2,_) = t1 <= t2
mk_switch :: CmmExpr -> [(ConTagZ, BlockId)]
-> Maybe BlockId
-> ConTagZ -> ConTagZ -> Bool
-> CmmAGraph
mk_switch _tag_expr [(tag, lbl)] _ lo_tag hi_tag _via_C
| lo_tag == hi_tag
= ASSERT( tag == lo_tag )
mkBranch lbl
mk_switch _tag_expr [(_tag,lbl)] Nothing _ _ _
= mkBranch lbl
mk_switch tag_expr [(tag,lbl)] (Just deflt) _ _ _
= mkCbranch cond deflt lbl
where
cond = cmmNeWord tag_expr (CmmLit (mkIntCLit tag))
mk_switch tag_expr branches mb_deflt lo_tag hi_tag via_C
| use_switch
= let
find_branch :: ConTagZ -> Maybe BlockId
find_branch i = case (assocMaybe branches i) of
Just lbl -> Just lbl
Nothing -> mb_deflt
arms :: [Maybe BlockId]
arms = [ find_branch i | i <- [real_lo_tag..real_hi_tag]]
in
mkSwitch (cmmOffset tag_expr ( real_lo_tag)) arms
| Just deflt <- mb_deflt, (lowest_branch lo_tag) >= n_branches
= mkCmmIfThenElse
(cmmULtWord tag_expr (CmmLit (mkIntCLit lowest_branch)))
(mkBranch deflt)
(mk_switch tag_expr branches mb_deflt
lowest_branch hi_tag via_C)
| Just deflt <- mb_deflt, (hi_tag highest_branch) >= n_branches
= mkCmmIfThenElse
(cmmUGtWord tag_expr (CmmLit (mkIntCLit highest_branch)))
(mkBranch deflt)
(mk_switch tag_expr branches mb_deflt
lo_tag highest_branch via_C)
| otherwise
= mkCmmIfThenElse
(cmmUGeWord tag_expr (CmmLit (mkIntCLit mid_tag)))
(mk_switch tag_expr hi_branches mb_deflt
mid_tag hi_tag via_C)
(mk_switch tag_expr lo_branches mb_deflt
lo_tag (mid_tag1) via_C)
where
use_switch =
ASSERT( n_branches > 1 && n_tags > 1 )
n_tags > 2 && (via_C || (dense && big_enough))
big_enough = n_branches > 4
dense = n_branches > (n_tags `div` 2)
n_branches = length branches
lowest_branch = fst (head branches)
highest_branch = fst (last branches)
real_lo_tag
| isNothing mb_deflt = lowest_branch
| otherwise = lo_tag
real_hi_tag
| isNothing mb_deflt = highest_branch
| otherwise = hi_tag
n_tags = real_hi_tag real_lo_tag + 1
(mid_tag,_) = branches !! (n_branches `div` 2)
(lo_branches, hi_branches) = span is_lo branches
is_lo (t,_) = t < mid_tag
mkCmmLitSwitch :: CmmExpr
-> [(Literal, CmmAGraph)]
-> CmmAGraph
-> CmmAGraph
mkCmmLitSwitch _scrut [] deflt = deflt
mkCmmLitSwitch scrut branches deflt
= assignTemp' scrut $ \ scrut' ->
withFreshLabel "switch join" $ \ join_lbl ->
label_code join_lbl deflt $ \ deflt ->
label_branches join_lbl branches $ \ branches ->
mk_lit_switch scrut' deflt (sortLe le branches)
<*> mkLabel join_lbl
where
le (t1,_) (t2,_) = t1 <= t2
mk_lit_switch :: CmmExpr -> BlockId
-> [(Literal,BlockId)]
-> CmmAGraph
mk_lit_switch scrut deflt [(lit,blk)]
= mkCbranch (CmmMachOp ne [scrut, CmmLit cmm_lit]) deflt blk
where
cmm_lit = mkSimpleLit lit
cmm_ty = cmmLitType cmm_lit
rep = typeWidth cmm_ty
ne = if isFloatType cmm_ty then MO_F_Ne rep else MO_Ne rep
mk_lit_switch scrut deflt_blk_id branches
= mkCmmIfThenElse cond
(mk_lit_switch scrut deflt_blk_id lo_branches)
(mk_lit_switch scrut deflt_blk_id hi_branches)
where
n_branches = length branches
(mid_lit,_) = branches !! (n_branches `div` 2)
(lo_branches, hi_branches) = span is_lo branches
is_lo (t,_) = t < mid_lit
cond = CmmMachOp (mkLtOp mid_lit)
[scrut, CmmLit (mkSimpleLit mid_lit)]
label_default :: BlockId -> Maybe CmmAGraph
-> (Maybe BlockId -> CmmAGraph)
-> CmmAGraph
label_default _ Nothing thing_inside
= thing_inside Nothing
label_default join_lbl (Just code) thing_inside
= label_code join_lbl code $ \ lbl ->
thing_inside (Just lbl)
label_branches :: BlockId -> [(a,CmmAGraph)]
-> ([(a,BlockId)] -> CmmAGraph)
-> CmmAGraph
label_branches _join_lbl [] thing_inside
= thing_inside []
label_branches join_lbl ((tag,code):branches) thing_inside
= label_code join_lbl code $ \ lbl ->
label_branches join_lbl branches $ \ branches' ->
thing_inside ((tag,lbl):branches')
label_code :: BlockId -> CmmAGraph -> (BlockId -> CmmAGraph) -> CmmAGraph
label_code join_lbl code thing_inside
= withFreshLabel "switch" $ \lbl ->
outOfLine (mkLabel lbl <*> code <*> mkBranch join_lbl)
<*> thing_inside lbl
assignTemp' :: CmmExpr -> (CmmExpr -> CmmAGraph) -> CmmAGraph
assignTemp' e thing_inside
| isTrivialCmmExpr e = thing_inside e
| otherwise = withTemp (cmmExprType e) $ \ lreg ->
let reg = CmmLocal lreg in
mkAssign reg e <*> thing_inside (CmmReg reg)
withTemp :: CmmType -> (LocalReg -> CmmAGraph) -> CmmAGraph
withTemp rep thing_inside
= withUnique $ \uniq -> thing_inside (LocalReg uniq rep)
getSRTInfo :: SRT -> FCode C_SRT
getSRTInfo (SRTEntries {}) = panic "getSRTInfo"
getSRTInfo (SRT off len bmp)
| len > hALF_WORD_SIZE_IN_BITS || bmp == [fromIntegral srt_escape]
= do { id <- newUnique
; let srt_desc_lbl = mkLargeSRTLabel id
; return (C_SRT srt_desc_lbl 0 srt_escape) }
| otherwise
= do { top_srt <- getSRTLabel
; return (C_SRT top_srt off (fromIntegral (head bmp))) }
getSRTInfo NoSRT
=
return NoC_SRT
srt_escape :: StgHalfWord
srt_escape = 1