1 {-# LANGUAGE BangPatterns, CPP, GADTs #-}
4 ( CmmAGraph
, CmmAGraphScoped
, CgStmt
(..)
6 , mkLabel
, mkMiddle
, mkLast
, outOfLine
7 , lgraphOfAGraph
, labelAGraph
10 , mkNop
, mkAssign
, mkStore
11 , mkUnsafeCall
, mkFinalCall
, mkCallReturnsTo
16 , mkReturn
, mkComment
, mkCallEntry
, mkBranch
18 , copyInOflow
, copyOutOflow
20 , toCall
, Transfer
(..)
24 import GhcPrelude
(($),Int,Bool,Eq
(..)) -- avoid importing (<*>)
29 import CmmSwitch
(SwitchTargets
)
38 import SMRep
(ByteOff
)
46 -----------------------------------------------------------------------------
50 -- | CmmAGraph is a chunk of code consisting of:
52 -- * ordinary statements (assignments, stores etc.)
55 -- * out-of-line labelled blocks
57 -- The semantics is that control falls through labels and out-of-line
58 -- blocks. Everything after a jump up to the next label is by
59 -- definition unreachable code, and will be discarded.
61 -- Two CmmAGraphs can be stuck together with <*>, with the meaning that
62 -- control flows from the first to the second.
64 -- A 'CmmAGraph' can be turned into a 'CmmGraph' (closed at both ends)
65 -- by providing a label for the entry point and a tick scope; see
67 type CmmAGraph
= OrdList CgStmt
68 -- | Unlabeled graph with tick scope
69 type CmmAGraphScoped
= (CmmAGraph
, CmmTickScope
)
72 = CgLabel BlockId CmmTickScope
73 | CgStmt
(CmmNode O O
)
74 | CgLast
(CmmNode O C
)
75 | CgFork BlockId CmmAGraph CmmTickScope
77 flattenCmmAGraph
:: BlockId
-> CmmAGraphScoped
-> CmmGraph
78 flattenCmmAGraph
id (stmts_t
, tscope
) =
79 CmmGraph
{ g_entry
= id,
80 g_graph
= GMany NothingO body NothingO
}
82 body
= foldr addBlock emptyBody
$ flatten
id stmts_t tscope
[]
85 -- flatten: given an entry label and a CmmAGraph, make a list of blocks.
87 -- NB. avoid the quadratic-append trap by passing in the tail of the
88 -- list. This is important for Very Long Functions (e.g. in T783).
90 flatten
:: Label
-> CmmAGraph
-> CmmTickScope
-> [Block CmmNode C C
]
91 -> [Block CmmNode C C
]
92 flatten
id g tscope blocks
93 = flatten1
(fromOL g
) block
' blocks
94 where !block
' = blockJoinHead
(CmmEntry
id tscope
) emptyBlock
96 -- flatten0: we are outside a block at this point: any code before
97 -- the first label is unreachable, so just drop it.
99 flatten0
:: [CgStmt
] -> [Block CmmNode C C
] -> [Block CmmNode C C
]
100 flatten0
[] blocks
= blocks
102 flatten0
(CgLabel
id tscope
: stmts
) blocks
103 = flatten1 stmts block blocks
104 where !block
= blockJoinHead
(CmmEntry
id tscope
) emptyBlock
106 flatten0
(CgFork fork_id stmts_t tscope
: rest
) blocks
107 = flatten fork_id stmts_t tscope
$ flatten0 rest blocks
109 flatten0
(CgLast _
: stmts
) blocks
= flatten0 stmts blocks
110 flatten0
(CgStmt _
: stmts
) blocks
= flatten0 stmts blocks
113 -- flatten1: we have a partial block, collect statements until the
114 -- next last node to make a block, then call flatten0 to get the rest
117 flatten1
:: [CgStmt
] -> Block CmmNode C O
118 -> [Block CmmNode C C
] -> [Block CmmNode C C
]
120 -- The current block falls through to the end of a function or fork:
121 -- this code should not be reachable, but it may be referenced by
122 -- other code that is not reachable. We'll remove it later with
123 -- dead-code analysis, but for now we have to keep the graph
124 -- well-formed, so we terminate the block with a branch to the
125 -- beginning of the current block.
126 flatten1
[] block blocks
127 = blockJoinTail block
(CmmBranch
(entryLabel block
)) : blocks
129 flatten1
(CgLast stmt
: stmts
) block blocks
130 = block
' : flatten0 stmts blocks
131 where !block
' = blockJoinTail block stmt
133 flatten1
(CgStmt stmt
: stmts
) block blocks
134 = flatten1 stmts block
' blocks
135 where !block
' = blockSnoc block stmt
137 flatten1
(CgFork fork_id stmts_t tscope
: rest
) block blocks
138 = flatten fork_id stmts_t tscope
$ flatten1 rest block blocks
140 -- a label here means that we should start a new block, and the
141 -- current block should fall through to the new block.
142 flatten1
(CgLabel
id tscp
: stmts
) block blocks
143 = blockJoinTail block
(CmmBranch
id) :
144 flatten1 stmts
(blockJoinHead
(CmmEntry
id tscp
) emptyBlock
) blocks
148 ---------- AGraph manipulation
150 (<*>) :: CmmAGraph
-> CmmAGraph
-> CmmAGraph
153 catAGraphs
:: [CmmAGraph
] -> CmmAGraph
154 catAGraphs
= concatOL
156 -- | created a sequence "goto id; id:" as an AGraph
157 mkLabel
:: BlockId
-> CmmTickScope
-> CmmAGraph
158 mkLabel bid scp
= unitOL
(CgLabel bid scp
)
160 -- | creates an open AGraph from a given node
161 mkMiddle
:: CmmNode O O
-> CmmAGraph
162 mkMiddle middle
= unitOL
(CgStmt middle
)
164 -- | created a closed AGraph from a given node
165 mkLast
:: CmmNode O C
-> CmmAGraph
166 mkLast
last = unitOL
(CgLast
last)
168 -- | A labelled code block; should end in a last node
169 outOfLine
:: BlockId
-> CmmAGraphScoped
-> CmmAGraph
170 outOfLine l
(c
,s
) = unitOL
(CgFork l c s
)
172 -- | allocate a fresh label for the entry point
173 lgraphOfAGraph
:: CmmAGraphScoped
-> UniqSM CmmGraph
174 lgraphOfAGraph g
= do
176 return (labelAGraph
(mkBlockId u
) g
)
178 -- | use the given BlockId as the label of the entry point
179 labelAGraph
:: BlockId
-> CmmAGraphScoped
-> CmmGraph
180 labelAGraph lbl ag
= flattenCmmAGraph lbl ag
186 mkComment
:: FastString
-> CmmAGraph
188 -- SDM: generating all those comments takes time, this saved about 4% for me
189 mkComment fs
= mkMiddle
$ CmmComment fs
194 ---------- Assignment and store
195 mkAssign
:: CmmReg
-> CmmExpr
-> CmmAGraph
196 mkAssign l
(CmmReg r
) | l
== r
= mkNop
197 mkAssign l r
= mkMiddle
$ CmmAssign l r
199 mkStore
:: CmmExpr
-> CmmExpr
-> CmmAGraph
200 mkStore l r
= mkMiddle
$ CmmStore l r
202 ---------- Control transfer
203 mkJump
:: DynFlags
-> Convention
-> CmmExpr
207 mkJump dflags conv e actuals updfr_off
=
208 lastWithArgs dflags Jump Old conv actuals updfr_off
$
209 toCall e Nothing updfr_off
0
211 -- | A jump where the caller says what the live GlobalRegs are. Used
212 -- for low-level hand-written Cmm.
213 mkRawJump
:: DynFlags
-> CmmExpr
-> UpdFrameOffset
-> [GlobalReg
]
215 mkRawJump dflags e updfr_off vols
=
216 lastWithArgs dflags Jump Old NativeNodeCall
[] updfr_off
$
217 \arg_space _
-> toCall e Nothing updfr_off
0 arg_space vols
220 mkJumpExtra
:: DynFlags
-> Convention
-> CmmExpr
-> [CmmExpr
]
221 -> UpdFrameOffset
-> [CmmExpr
]
223 mkJumpExtra dflags conv e actuals updfr_off extra_stack
=
224 lastWithArgsAndExtraStack dflags Jump Old conv actuals updfr_off extra_stack
$
225 toCall e Nothing updfr_off
0
227 mkCbranch
:: CmmExpr
-> BlockId
-> BlockId
-> Maybe Bool -> CmmAGraph
228 mkCbranch
pred ifso ifnot likely
=
229 mkLast
(CmmCondBranch
pred ifso ifnot likely
)
231 mkSwitch
:: CmmExpr
-> SwitchTargets
-> CmmAGraph
232 mkSwitch e tbl
= mkLast
$ CmmSwitch e tbl
234 mkReturn
:: DynFlags
-> CmmExpr
-> [CmmExpr
] -> UpdFrameOffset
236 mkReturn dflags e actuals updfr_off
=
237 lastWithArgs dflags Ret Old NativeReturn actuals updfr_off
$
238 toCall e Nothing updfr_off
0
240 mkBranch
:: BlockId
-> CmmAGraph
241 mkBranch bid
= mkLast
(CmmBranch bid
)
243 mkFinalCall
:: DynFlags
244 -> CmmExpr
-> CCallConv
-> [CmmExpr
] -> UpdFrameOffset
246 mkFinalCall dflags f _ actuals updfr_off
=
247 lastWithArgs dflags Call Old NativeDirectCall actuals updfr_off
$
248 toCall f Nothing updfr_off
0
250 mkCallReturnsTo
:: DynFlags
-> CmmExpr
-> Convention
-> [CmmExpr
]
256 mkCallReturnsTo dflags f callConv actuals ret_lbl ret_off updfr_off extra_stack
= do
257 lastWithArgsAndExtraStack dflags Call
(Young ret_lbl
) callConv actuals
258 updfr_off extra_stack
$
259 toCall f
(Just ret_lbl
) updfr_off ret_off
261 -- Like mkCallReturnsTo, but does not push the return address (it is assumed to be
262 -- already on the stack).
263 mkJumpReturnsTo
:: DynFlags
-> CmmExpr
-> Convention
-> [CmmExpr
]
268 mkJumpReturnsTo dflags f callConv actuals ret_lbl ret_off updfr_off
= do
269 lastWithArgs dflags JumpRet
(Young ret_lbl
) callConv actuals updfr_off
$
270 toCall f
(Just ret_lbl
) updfr_off ret_off
272 mkUnsafeCall
:: ForeignTarget
-> [CmmFormal
] -> [CmmActual
] -> CmmAGraph
273 mkUnsafeCall t fs
as = mkMiddle
$ CmmUnsafeForeignCall t fs
as
275 -- | Construct a 'CmmUnwind' node for the given register and unwinding
277 mkUnwind
:: GlobalReg
-> CmmExpr
-> CmmAGraph
278 mkUnwind r e
= mkMiddle
$ CmmUnwind
[(r
, Just e
)]
280 --------------------------------------------------------------------------
285 -- Why are we inserting extra blocks that simply branch to the successors?
286 -- Because in addition to the branch instruction, @mkBranch@ will insert
287 -- a necessary adjustment to the stack pointer.
290 -- For debugging purposes, we can stub out dead stack slots:
291 stackStubExpr
:: Width
-> CmmExpr
292 stackStubExpr w
= CmmLit
(CmmInt
0 w
)
294 -- When we copy in parameters, we usually want to put overflow
295 -- parameters on the stack, but sometimes we want to pass the
296 -- variables in their spill slots. Therefore, for copying arguments
297 -- and results, we provide different functions to pass the arguments
298 -- in an overflow area and to pass them in spill slots.
299 copyInOflow
:: DynFlags
-> Convention
-> Area
302 -> (Int, [GlobalReg
], CmmAGraph
)
304 copyInOflow dflags conv area formals extra_stk
305 = (offset
, gregs
, catAGraphs
$ map mkMiddle nodes
)
306 where (offset
, gregs
, nodes
) = copyIn dflags conv area formals extra_stk
308 -- Return the number of bytes used for copying arguments, as well as the
309 -- instructions to copy the arguments.
310 copyIn
:: DynFlags
-> Convention
-> Area
313 -> (ByteOff
, [GlobalReg
], [CmmNode O O
])
314 copyIn dflags conv area formals extra_stk
315 = (stk_size
, [r |
(_
, RegisterParam r
) <- args
], map ci
(stk_args
++ args
))
317 ci
(reg
, RegisterParam r
) =
318 CmmAssign
(CmmLocal reg
) (CmmReg
(CmmGlobal r
))
319 ci
(reg
, StackParam off
) =
320 CmmAssign
(CmmLocal reg
) (CmmLoad
(CmmStackSlot area off
) ty
)
321 where ty
= localRegType reg
323 init_offset
= widthInBytes
(wordWidth dflags
) -- infotable
325 (stk_off
, stk_args
) = assignStack dflags init_offset localRegType extra_stk
327 (stk_size
, args
) = assignArgumentsPos dflags stk_off conv
330 -- Factoring out the common parts of the copyout functions yielded something
333 data Transfer
= Call | JumpRet | Jump | Ret
deriving Eq
335 copyOutOflow
:: DynFlags
-> Convention
-> Transfer
-> Area
-> [CmmExpr
]
337 -> [CmmExpr
] -- extra stack args
338 -> (Int, [GlobalReg
], CmmAGraph
)
340 -- Generate code to move the actual parameters into the locations
341 -- required by the calling convention. This includes a store for the
344 -- The argument layout function ignores the pointer to the info table,
345 -- so we slot that in here. When copying-out to a young area, we set
346 -- the info table for return and adjust the offsets of the other
347 -- parameters. If this is a call instruction, we adjust the offsets
348 -- of the other parameters.
349 copyOutOflow dflags conv transfer area actuals updfr_off extra_stack_stuff
350 = (stk_size
, regs
, graph
)
352 (regs
, graph
) = foldr co
([], mkNop
) (setRA
++ args
++ stack_params
)
354 co
(v
, RegisterParam r
) (rs
, ms
)
355 = (r
:rs
, mkAssign
(CmmGlobal r
) v
<*> ms
)
356 co
(v
, StackParam off
) (rs
, ms
)
357 = (rs
, mkStore
(CmmStackSlot area off
) v
<*> ms
)
359 (setRA
, init_offset
) =
361 Young
id -> -- Generate a store instruction for
362 -- the return address if making a call
365 ([(CmmLit
(CmmBlock
id), StackParam init_offset
)],
366 widthInBytes
(wordWidth dflags
))
369 widthInBytes
(wordWidth dflags
))
372 Old
-> ([], updfr_off
)
374 (extra_stack_off
, stack_params
) =
375 assignStack dflags init_offset
(cmmExprType dflags
) extra_stack_stuff
377 args
:: [(CmmExpr
, ParamLocation
)] -- The argument and where to put it
378 (stk_size
, args
) = assignArgumentsPos dflags extra_stack_off conv
379 (cmmExprType dflags
) actuals
383 mkCallEntry
:: DynFlags
-> Convention
-> [CmmFormal
] -> [CmmFormal
]
384 -> (Int, [GlobalReg
], CmmAGraph
)
385 mkCallEntry dflags conv formals extra_stk
386 = copyInOflow dflags conv Old formals extra_stk
388 lastWithArgs
:: DynFlags
-> Transfer
-> Area
-> Convention
-> [CmmExpr
]
390 -> (ByteOff
-> [GlobalReg
] -> CmmAGraph
)
392 lastWithArgs dflags transfer area conv actuals updfr_off
last =
393 lastWithArgsAndExtraStack dflags transfer area conv actuals
394 updfr_off noExtraStack
last
396 lastWithArgsAndExtraStack
:: DynFlags
397 -> Transfer
-> Area
-> Convention
-> [CmmExpr
]
398 -> UpdFrameOffset
-> [CmmExpr
]
399 -> (ByteOff
-> [GlobalReg
] -> CmmAGraph
)
401 lastWithArgsAndExtraStack dflags transfer area conv actuals updfr_off
403 copies
<*> last outArgs regs
405 (outArgs
, regs
, copies
) = copyOutOflow dflags conv transfer area actuals
406 updfr_off extra_stack
409 noExtraStack
:: [CmmExpr
]
412 toCall
:: CmmExpr
-> Maybe BlockId
-> UpdFrameOffset
-> ByteOff
413 -> ByteOff
-> [GlobalReg
]
415 toCall e cont updfr_off res_space arg_space regs
=
416 mkLast
$ CmmCall e cont regs arg_space res_space updfr_off