1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
|
{-# OPTIONS_GHC -XNoMonoLocalBinds #-}
-- Norman likes local bindings
-- This module is pure representation and should be imported only by
-- clients that need to manipulate representation and know what
-- they're doing. Clients that need to create flow graphs should
-- instead import MkZipCfgCmm.
module ZipCfgCmmRep
( CmmZ, CmmTopZ, CmmGraph, CmmBlock, CmmAGraph
, Middle(..), Last(..), MidCallTarget(..), UpdFrameOffset
, Convention(..), ForeignConvention(..), ForeignSafety(..)
, ValueDirection(..), ForeignHint(..)
, CmmBackwardFixedPoint, CmmForwardFixedPoint, pprHinted
, insertBetween, mapExpMiddle, mapExpLast, mapExpDeepMiddle, mapExpDeepLast
, foldExpMiddle, foldExpLast, foldExpDeepMiddle, foldExpDeepLast, joinOuts
)
where
import BlockId
import CmmExpr
import Cmm ( GenCmm(..), GenCmmTop(..), CmmStatic, CmmInfo
, CallishMachOp(..), ForeignHint(..)
, CmmActuals, CmmFormals, CmmHinted(..)
, CmmStmt(..) -- imported in order to call ppr on Switch and to
-- implement pprCmmGraphLikeCmm
)
import DFMonad
import PprCmm()
import CmmTx
import CLabel
import FastString
import ForeignCall
import qualified ZipDataflow as DF
import ZipCfg
import MkZipCfg
import Util
import BasicTypes
import Maybes
import Control.Monad
import Outputable
import Prelude hiding (zip, unzip, last)
import SMRep (ByteOff)
import UniqSupply
----------------------------------------------------------------------
----- Type synonyms and definitions
type CmmGraph = LGraph Middle Last
type CmmAGraph = AGraph Middle Last
type CmmBlock = Block Middle Last
type CmmStackInfo = (ByteOff, Maybe ByteOff)
-- probably want a record; (SP offset on entry, update frame space)
type CmmZ = GenCmm CmmStatic CmmInfo (CmmStackInfo, CmmGraph)
type CmmTopZ = GenCmmTop CmmStatic CmmInfo (CmmStackInfo, CmmGraph)
type CmmBackwardFixedPoint a = DF.BackwardFixedPoint Middle Last a ()
type CmmForwardFixedPoint a = DF.ForwardFixedPoint Middle Last a ()
type UpdFrameOffset = ByteOff
data Middle
= MidComment FastString
| MidAssign CmmReg CmmExpr -- Assign to register
| MidStore CmmExpr CmmExpr -- Assign to memory location. Size is
-- given by cmmExprType of the rhs.
| MidForeignCall -- A foreign call; see Note [Foreign calls]
ForeignSafety -- Is it a safe or unsafe call?
MidCallTarget -- call target and convention
CmmFormals -- zero or more results
CmmActuals -- zero or more arguments
deriving Eq
data Last
= LastBranch BlockId -- Goto another block in the same procedure
| LastCondBranch { -- conditional branch
cml_pred :: CmmExpr,
cml_true, cml_false :: BlockId
}
| LastSwitch CmmExpr [Maybe BlockId] -- Table branch
-- The scrutinee is zero-based;
-- zero -> first block
-- one -> second block etc
-- Undefined outside range, and when there's a Nothing
| LastCall { -- A call (native or safe foreign)
cml_target :: CmmExpr, -- never a CmmPrim to a CallishMachOp!
cml_cont :: Maybe BlockId,
-- BlockId of continuation (Nothing for return or tail call)
cml_args :: ByteOff,
-- Byte offset, from the *old* end of the Area associated with
-- the BlockId (if cml_cont = Nothing, then Old area), of
-- youngest outgoing arg. Set the stack pointer to this before
-- transferring control.
-- (NB: an update frame might also have been stored in the Old
-- area, but it'll be in an older part than the args.)
cml_ret_args :: ByteOff,
-- For calls *only*, the byte offset for youngest returned value
-- This is really needed at the *return* point rather than here
-- at the call, but in practice it's convenient to record it here.
cml_ret_off :: Maybe ByteOff
-- For calls *only*, the byte offset of the base of the frame that
-- must be described by the info table for the return point.
-- The older words are an update frames, which have their own
-- info-table and layout information
-- From a liveness point of view, the stack words older than
-- cml_ret_off are treated as live, even if the sequel of
-- the call goes into a loop.
}
data MidCallTarget -- The target of a MidUnsafeCall
= ForeignTarget -- A foreign procedure
CmmExpr -- Its address
ForeignConvention -- Its calling convention
| PrimTarget -- A possibly-side-effecting machine operation
CallishMachOp -- Which one
deriving Eq
data Convention
= NativeDirectCall -- Native C-- call skipping the node (closure) argument
| NativeNodeCall -- Native C-- call including the node argument
| NativeReturn -- Native C-- return
| Slow -- Slow entry points: all args pushed on the stack
| GC -- Entry to the garbage collector: uses the node reg!
| PrimOpCall -- Calling prim ops
| PrimOpReturn -- Returning from prim ops
| Foreign -- Foreign call/return
ForeignConvention
| Private
-- Used for control transfers within a (pre-CPS) procedure All
-- jump sites known, never pushed on the stack (hence no SRT)
-- You can choose whatever calling convention you please
-- (provided you make sure all the call sites agree)!
-- This data type eventually to be extended to record the convention.
deriving( Eq )
data ForeignConvention
= ForeignConvention
CCallConv -- Which foreign-call convention
[ForeignHint] -- Extra info about the args
[ForeignHint] -- Extra info about the result
deriving Eq
data ForeignSafety
= Unsafe -- unsafe call
| Safe BlockId -- making infotable requires: 1. label
UpdFrameOffset -- 2. where the upd frame is
deriving Eq
data ValueDirection = Arguments | Results
-- Arguments go with procedure definitions, jumps, and arguments to calls
-- Results go with returns and with results of calls.
deriving Eq
{- Note [Foreign calls]
~~~~~~~~~~~~~~~~~~~~~~~
A MidForeign call is used *all* foreign calls, both *unsafe* and *safe*.
Unsafe ones are easy: think of them as a "fat machine instruction".
Safe ones are trickier. A safe foreign call
r = f(x)
ultimately expands to
push "return address" -- Never used to return to;
-- just points an info table
save registers into TSO
call suspendThread
r = f(x) -- Make the call
call resumeThread
restore registers
pop "return address"
We cannot "lower" a safe foreign call to this sequence of Cmms, because
after we've saved Sp all the Cmm optimiser's assumptions are broken.
Furthermore, currently the smart Cmm constructors know the calling
conventions for Haskell, the garbage collector, etc, and "lower" them
so that a LastCall passes no parameters or results. But the smart
constructors do *not* (currently) know the foreign call conventions.
For these reasons use MidForeignCall for all calls. The only annoying thing
is that a safe foreign call needs an info table.
-}
----------------------------------------------------------------------
----- Splicing between blocks
-- Given a middle node, a block, and a successor BlockId,
-- we can insert the middle node between the block and the successor.
-- We return the updated block and a list of new blocks that must be added
-- to the graph.
-- The semantics is a bit tricky. We consider cases on the last node:
-- o For a branch, we can just insert before the branch,
-- but sometimes the optimizer does better if we actually insert
-- a fresh basic block, enabling some common blockification.
-- o For a conditional branch, switch statement, or call, we must insert
-- a new basic block.
-- o For a jump or return, this operation is impossible.
insertBetween :: MonadUnique m => CmmBlock -> [Middle] -> BlockId -> m (CmmBlock, [CmmBlock])
insertBetween b ms succId = insert $ goto_end $ unzip b
where insert (h, LastOther (LastBranch bid)) =
if bid == succId then
do (bid', bs) <- newBlocks
return (zipht h (ZLast (LastOther (LastBranch bid'))), bs)
else panic "tried invalid block insertBetween"
insert (h, LastOther (LastCondBranch c t f)) =
do (t', tbs) <- if t == succId then newBlocks else return $ (t, [])
(f', fbs) <- if f == succId then newBlocks else return $ (f, [])
return (zipht h $ ZLast $ LastOther (LastCondBranch c t' f'), tbs ++ fbs)
insert (h, LastOther (LastSwitch e ks)) =
do (ids, bs) <- mapAndUnzipM mbNewBlocks ks
return (zipht h $ ZLast $ LastOther (LastSwitch e ids), join bs)
insert (_, LastOther (LastCall {})) =
panic "unimp: insertBetween after a call -- probably not a good idea"
insert (_, LastExit) = panic "cannot insert after exit"
newBlocks = do id <- liftM BlockId $ getUniqueM
return $ (id, [Block id $
foldr ZTail (ZLast (LastOther (LastBranch succId))) ms])
mbNewBlocks (Just k) = if k == succId then liftM lift newBlocks
else return (Just k, [])
mbNewBlocks Nothing = return (Nothing, [])
lift (id, bs) = (Just id, bs)
----------------------------------------------------------------------
----- Instance declarations for control flow
instance HavingSuccessors Last where
succs = cmmSuccs
fold_succs = fold_cmm_succs
instance LastNode Last where
mkBranchNode id = LastBranch id
isBranchNode (LastBranch _) = True
isBranchNode _ = False
branchNodeTarget (LastBranch id) = id
branchNodeTarget _ = panic "asked for target of non-branch"
cmmSuccs :: Last -> [BlockId]
cmmSuccs (LastBranch id) = [id]
cmmSuccs (LastCall _ Nothing _ _ _) = []
cmmSuccs (LastCall _ (Just id) _ _ _) = [id]
cmmSuccs (LastCondBranch _ t f) = [f, t] -- meets layout constraint
cmmSuccs (LastSwitch _ edges) = catMaybes edges
fold_cmm_succs :: (BlockId -> a -> a) -> Last -> a -> a
fold_cmm_succs f (LastBranch id) z = f id z
fold_cmm_succs _ (LastCall _ Nothing _ _ _) z = z
fold_cmm_succs f (LastCall _ (Just id) _ _ _) z = f id z
fold_cmm_succs f (LastCondBranch _ te fe) z = f te (f fe z)
fold_cmm_succs f (LastSwitch _ edges) z = foldl (flip f) z $ catMaybes edges
----------------------------------------------------------------------
----- Instance declarations for register use
instance UserOfLocalRegs Middle where
foldRegsUsed f z m = middle m
where middle (MidComment {}) = z
middle (MidAssign _lhs expr) = fold f z expr
middle (MidStore addr rval) = fold f (fold f z addr) rval
middle (MidForeignCall _ tgt _ args) = fold f (fold f z tgt) args
fold f z m = foldRegsUsed f z m -- avoid monomorphism restriction
instance UserOfLocalRegs MidCallTarget where
foldRegsUsed _f z (PrimTarget _) = z
foldRegsUsed f z (ForeignTarget e _) = foldRegsUsed f z e
instance UserOfSlots MidCallTarget where
foldSlotsUsed f z (ForeignTarget e _) = foldSlotsUsed f z e
foldSlotsUsed _f z (PrimTarget _) = z
instance (UserOfLocalRegs a) => UserOfLocalRegs (Maybe a) where
foldRegsUsed f z (Just x) = foldRegsUsed f z x
foldRegsUsed _ z Nothing = z
instance (UserOfSlots a) => UserOfSlots (Maybe a) where
foldSlotsUsed f z (Just x) = foldSlotsUsed f z x
foldSlotsUsed _ z Nothing = z
instance UserOfLocalRegs Last where
foldRegsUsed f z l = last l
where last (LastBranch _id) = z
last (LastCall tgt _ _ _ _) = foldRegsUsed f z tgt
last (LastCondBranch e _ _) = foldRegsUsed f z e
last (LastSwitch e _tbl) = foldRegsUsed f z e
instance DefinerOfLocalRegs Middle where
foldRegsDefd f z m = middle m
where middle (MidComment {}) = z
middle (MidAssign lhs _) = fold f z lhs
middle (MidStore _ _) = z
middle (MidForeignCall _ _ fs _) = fold f z fs
fold f z m = foldRegsDefd f z m -- avoid monomorphism restriction
instance DefinerOfLocalRegs Last where
foldRegsDefd _ z _ = z
----------------------------------------------------------------------
----- Instance declarations for stack slot use
instance UserOfSlots Middle where
foldSlotsUsed f z m = middle m
where middle (MidComment {}) = z
middle (MidAssign _lhs expr) = fold f z expr
middle (MidStore addr rval) = fold f (fold f z addr) rval
middle (MidForeignCall _ tgt _ress args) = fold f (fold f z tgt) args
fold f z e = foldSlotsUsed f z e -- avoid monomorphism restriction
instance UserOfSlots Last where
foldSlotsUsed f z l = last l
where last (LastBranch _id) = z
last (LastCall tgt _ _ _ _) = foldSlotsUsed f z tgt
last (LastCondBranch e _ _) = foldSlotsUsed f z e
last (LastSwitch e _tbl) = foldSlotsUsed f z e
instance UserOfSlots l => UserOfSlots (ZLast l) where
foldSlotsUsed f z (LastOther l) = foldSlotsUsed f z l
foldSlotsUsed _ z LastExit = z
instance DefinerOfSlots Middle where
foldSlotsDefd f z m = middle m
where middle (MidComment {}) = z
middle (MidAssign _ _) = z
middle (MidForeignCall {}) = z
middle (MidStore (CmmStackSlot a i) e) =
f z (a, i, widthInBytes $ typeWidth $ cmmExprType e)
middle (MidStore _ _) = z
instance DefinerOfSlots Last where
foldSlotsDefd _ z _ = z
instance DefinerOfSlots l => DefinerOfSlots (ZLast l) where
foldSlotsDefd f z (LastOther l) = foldSlotsDefd f z l
foldSlotsDefd _ z LastExit = z
----------------------------------------------------------------------
----- Code for manipulating Middle and Last nodes
mapExpMiddle :: (CmmExpr -> CmmExpr) -> Middle -> Middle
mapExpMiddle _ m@(MidComment _) = m
mapExpMiddle exp (MidAssign r e) = MidAssign r (exp e)
mapExpMiddle exp (MidStore addr e) = MidStore (exp addr) (exp e)
mapExpMiddle exp (MidForeignCall s tgt fs as) =
MidForeignCall s (mapExpMidcall exp tgt) fs (map exp as)
foldExpMiddle :: (CmmExpr -> z -> z) -> Middle -> z -> z
foldExpMiddle _ (MidComment _) z = z
foldExpMiddle exp (MidAssign _ e) z = exp e z
foldExpMiddle exp (MidStore addr e) z = exp addr $ exp e z
foldExpMiddle exp (MidForeignCall _ tgt _ as) z = foldExpMidcall exp tgt $ foldr exp z as
mapExpLast :: (CmmExpr -> CmmExpr) -> Last -> Last
mapExpLast _ l@(LastBranch _) = l
mapExpLast exp (LastCondBranch e ti fi) = LastCondBranch (exp e) ti fi
mapExpLast exp (LastSwitch e tbl) = LastSwitch (exp e) tbl
mapExpLast exp (LastCall tgt mb_id o i s) = LastCall (exp tgt) mb_id o i s
foldExpLast :: (CmmExpr -> z -> z) -> Last -> z -> z
foldExpLast _ (LastBranch _) z = z
foldExpLast exp (LastCondBranch e _ _) z = exp e z
foldExpLast exp (LastSwitch e _) z = exp e z
foldExpLast exp (LastCall tgt _ _ _ _) z = exp tgt z
mapExpMidcall :: (CmmExpr -> CmmExpr) -> MidCallTarget -> MidCallTarget
mapExpMidcall exp (ForeignTarget e c) = ForeignTarget (exp e) c
mapExpMidcall _ m@(PrimTarget _) = m
foldExpMidcall :: (CmmExpr -> z -> z) -> MidCallTarget -> z -> z
foldExpMidcall exp (ForeignTarget e _) z = exp e z
foldExpMidcall _ (PrimTarget _) z = z
-- Take a transformer on expressions and apply it recursively.
wrapRecExp :: (CmmExpr -> CmmExpr) -> CmmExpr -> CmmExpr
wrapRecExp f (CmmMachOp op es) = f (CmmMachOp op $ map (wrapRecExp f) es)
wrapRecExp f (CmmLoad addr ty) = f (CmmLoad (wrapRecExp f addr) ty)
wrapRecExp f e = f e
mapExpDeepMiddle :: (CmmExpr -> CmmExpr) -> Middle -> Middle
mapExpDeepLast :: (CmmExpr -> CmmExpr) -> Last -> Last
mapExpDeepMiddle f = mapExpMiddle $ wrapRecExp f
mapExpDeepLast f = mapExpLast $ wrapRecExp f
-- Take a folder on expressions and apply it recursively.
wrapRecExpf :: (CmmExpr -> z -> z) -> CmmExpr -> z -> z
wrapRecExpf f e@(CmmMachOp _ es) z = foldr (wrapRecExpf f) (f e z) es
wrapRecExpf f e@(CmmLoad addr _) z = wrapRecExpf f addr (f e z)
wrapRecExpf f e z = f e z
foldExpDeepMiddle :: (CmmExpr -> z -> z) -> Middle -> z -> z
foldExpDeepLast :: (CmmExpr -> z -> z) -> Last -> z -> z
foldExpDeepMiddle f = foldExpMiddle $ wrapRecExpf f
foldExpDeepLast f = foldExpLast $ wrapRecExpf f
----------------------------------------------------------------------
-- Compute the join of facts live out of a Last node. Useful for most backward
-- analyses.
joinOuts :: DataflowLattice a -> (BlockId -> a) -> Last -> a
joinOuts lattice env l =
let bot = fact_bot lattice
join x y = txVal $ fact_add_to lattice x y
in case l of
(LastBranch id) -> env id
(LastCall _ Nothing _ _ _) -> bot
(LastCall _ (Just k) _ _ _) -> env k
(LastCondBranch _ t f) -> join (env t) (env f)
(LastSwitch _ tbl) -> foldr join bot (map env $ catMaybes tbl)
----------------------------------------------------------------------
----- Instance declarations for prettyprinting (avoids recursive imports)
instance Outputable Middle where
ppr s = pprMiddle s
instance Outputable Last where
ppr s = pprLast s
instance Outputable Convention where
ppr = pprConvention
instance Outputable ForeignConvention where
ppr = pprForeignConvention
instance Outputable ValueDirection where
ppr Arguments = ptext $ sLit "args"
ppr Results = ptext $ sLit "results"
instance DF.DebugNodes Middle Last
debugPpr :: Bool
debugPpr = debugIsOn
pprMiddle :: Middle -> SDoc
pprMiddle stmt = pp_stmt <+> pp_debug
where
pp_stmt = case stmt of
-- // text
MidComment s -> text "//" <+> ftext s
-- reg = expr;
MidAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi
-- rep[lv] = expr;
MidStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi
where
rep = ppr ( cmmExprType expr )
-- call "ccall" foo(x, y)[r1, r2];
-- ToDo ppr volatile
MidForeignCall safety target results args ->
hsep [ ppUnless (null results) $
parens (commafy $ map ppr results) <+> equals,
ppr_safety safety,
ptext $ sLit "call",
ppr_call_target target <> parens (commafy $ map ppr args) <> semi]
pp_debug =
if not debugPpr then empty
else text " //" <+>
case stmt of
MidComment {} -> text "MidComment"
MidAssign {} -> text "MidAssign"
MidStore {} -> text "MidStore"
MidForeignCall {} -> text "MidForeignCall"
ppr_fc :: ForeignConvention -> SDoc
ppr_fc (ForeignConvention c args res) =
doubleQuotes (ppr c) <+> text "args: " <+> ppr args <+> text " results: " <+> ppr res
ppr_safety :: ForeignSafety -> SDoc
ppr_safety (Safe bid upd) = text "safe<" <> ppr bid <> text ", " <> ppr upd <> text ">"
ppr_safety Unsafe = text "unsafe"
ppr_call_target :: MidCallTarget -> SDoc
ppr_call_target (ForeignTarget fn c) = ppr_fc c <+> ppr_target fn
ppr_call_target (PrimTarget op)
-- HACK: We're just using a ForeignLabel to get this printed, the label
-- might not really be foreign.
= ppr (CmmLabel (mkForeignLabel
(mkFastString (show op))
Nothing ForeignLabelInThisPackage IsFunction))
ppr_target :: CmmExpr -> SDoc
ppr_target t@(CmmLit _) = ppr t
ppr_target fn' = parens (ppr fn')
pprHinted :: Outputable a => CmmHinted a -> SDoc
pprHinted (CmmHinted a NoHint) = ppr a
pprHinted (CmmHinted a AddrHint) = doubleQuotes (text "address") <+> ppr a
pprHinted (CmmHinted a SignedHint) = doubleQuotes (text "signed") <+> ppr a
pprLast :: Last -> SDoc
pprLast stmt = pp_stmt <+> pp_debug
where
pp_stmt = case stmt of
LastBranch ident -> ptext (sLit "goto") <+> ppr ident <> semi
LastCondBranch expr t f -> genFullCondBranch expr t f
LastSwitch arg ids -> ppr $ CmmSwitch arg ids
LastCall tgt k out res updfr_off -> genBareCall tgt k out res updfr_off
pp_debug = text " //" <+> case stmt of
LastBranch {} -> text "LastBranch"
LastCondBranch {} -> text "LastCondBranch"
LastSwitch {} -> text "LastSwitch"
LastCall {} -> text "LastCall"
genBareCall :: CmmExpr -> Maybe BlockId -> ByteOff -> ByteOff ->
Maybe UpdFrameOffset -> SDoc
genBareCall fn k out res updfr_off =
hcat [ ptext (sLit "call"), space
, pprFun fn, ptext (sLit "(...)"), space
, ptext (sLit "returns to") <+> ppr k <+> parens (ppr out)
<+> parens (ppr res)
, ptext (sLit " with update frame") <+> ppr updfr_off
, semi ]
pprFun :: CmmExpr -> SDoc
pprFun f@(CmmLit _) = ppr f
pprFun f = parens (ppr f)
genFullCondBranch :: Outputable id => CmmExpr -> id -> id -> SDoc
genFullCondBranch expr t f =
hsep [ ptext (sLit "if")
, parens(ppr expr)
, ptext (sLit "goto")
, ppr t <> semi
, ptext (sLit "else goto")
, ppr f <> semi
]
pprConvention :: Convention -> SDoc
pprConvention (NativeNodeCall {}) = text "<native-node-call-convention>"
pprConvention (NativeDirectCall {}) = text "<native-direct-call-convention>"
pprConvention (NativeReturn {}) = text "<native-ret-convention>"
pprConvention Slow = text "<slow-convention>"
pprConvention GC = text "<gc-convention>"
pprConvention PrimOpCall = text "<primop-call-convention>"
pprConvention PrimOpReturn = text "<primop-ret-convention>"
pprConvention (Foreign c) = ppr c
pprConvention (Private {}) = text "<private-convention>"
pprForeignConvention :: ForeignConvention -> SDoc
pprForeignConvention (ForeignConvention c as rs) = ppr c <> ppr as <> ppr rs
commafy :: [SDoc] -> SDoc
commafy xs = hsep $ punctuate comma xs
|