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
|
{-# LANGUAGE GADTs, TypeFamilies, FlexibleContexts, FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
----------------------------------------------------------------------------
--
-- Pretty-printing of Cmm as (a superset of) C--
--
-- (c) The University of Glasgow 2004-2006
--
-----------------------------------------------------------------------------
--
-- This is where we walk over CmmNode emitting an external representation,
-- suitable for parsing, in a syntax strongly reminiscent of C--. This
-- is the "External Core" for the Cmm layer.
--
-- As such, this should be a well-defined syntax: we want it to look nice.
-- Thus, we try wherever possible to use syntax defined in [1],
-- "The C-- Reference Manual", http://www.cs.tufts.edu/~nr/c--/index.html. We
-- differ slightly, in some cases. For one, we use I8 .. I64 for types, rather
-- than C--'s bits8 .. bits64.
--
-- We try to ensure that all information available in the abstract
-- syntax is reproduced, or reproducible, in the concrete syntax.
-- Data that is not in printed out can be reconstructed according to
-- conventions used in the pretty printer. There are at least two such
-- cases:
-- 1) if a value has wordRep type, the type is not appended in the
-- output.
-- 2) MachOps that operate over wordRep type are printed in a
-- C-style, rather than as their internal MachRep name.
--
-- These conventions produce much more readable Cmm output.
--
-- A useful example pass over Cmm is in nativeGen/MachCodeGen.hs
module GHC.Cmm.Ppr
( module GHC.Cmm.Ppr.Decl
, module GHC.Cmm.Ppr.Expr
)
where
import GHC.Prelude hiding (succ)
import GHC.Platform
import GHC.Cmm.CLabel
import GHC.Cmm
import GHC.Cmm.Utils
import GHC.Cmm.Switch
import GHC.Data.FastString
import GHC.Utils.Outputable
import GHC.Cmm.Ppr.Decl
import GHC.Cmm.Ppr.Expr
import GHC.Utils.Misc
import GHC.Types.Basic
import GHC.Cmm.Dataflow.Block
import GHC.Cmm.Dataflow.Graph
-------------------------------------------------
-- Outputable instances
instance Outputable CmmStackInfo where
ppr = pprStackInfo
instance OutputableP CmmTopInfo where
pdoc = pprTopInfo
instance OutputableP (CmmNode e x) where
pdoc = pprNode
instance Outputable Convention where
ppr = pprConvention
instance Outputable ForeignConvention where
ppr = pprForeignConvention
instance OutputableP ForeignTarget where
pdoc = pprForeignTarget
instance Outputable CmmReturnInfo where
ppr = pprReturnInfo
instance OutputableP (Block CmmNode C C) where
pdoc = pprBlock
instance OutputableP (Block CmmNode C O) where
pdoc = pprBlock
instance OutputableP (Block CmmNode O C) where
pdoc = pprBlock
instance OutputableP (Block CmmNode O O) where
pdoc = pprBlock
instance OutputableP (Graph CmmNode e x) where
pdoc = pprGraph
instance OutputableP CmmGraph where
pdoc = pprCmmGraph
----------------------------------------------------------
-- Outputting types Cmm contains
pprStackInfo :: CmmStackInfo -> SDoc
pprStackInfo (StackInfo {arg_space=arg_space}) =
text "arg_space: " <> ppr arg_space
pprTopInfo :: Platform -> CmmTopInfo -> SDoc
pprTopInfo platform (TopInfo {info_tbls=info_tbl, stack_info=stack_info}) =
vcat [text "info_tbls: " <> pdoc platform info_tbl,
text "stack_info: " <> ppr stack_info]
----------------------------------------------------------
-- Outputting blocks and graphs
pprBlock :: IndexedCO x SDoc SDoc ~ SDoc
=> Platform -> Block CmmNode e x -> IndexedCO e SDoc SDoc
pprBlock platform block
= foldBlockNodesB3 ( ($$) . pdoc platform
, ($$) . (nest 4) . pdoc platform
, ($$) . (nest 4) . pdoc platform
)
block
empty
pprGraph :: Platform -> Graph CmmNode e x -> SDoc
pprGraph platform = \case
GNil -> empty
GUnit block -> pdoc platform block
GMany entry body exit ->
text "{"
$$ nest 2 (pprMaybeO entry $$ (vcat $ map (pdoc platform) $ bodyToBlockList body) $$ pprMaybeO exit)
$$ text "}"
where pprMaybeO :: OutputableP (Block CmmNode e x)
=> MaybeO ex (Block CmmNode e x) -> SDoc
pprMaybeO NothingO = empty
pprMaybeO (JustO block) = pdoc platform block
pprCmmGraph :: Platform -> CmmGraph -> SDoc
pprCmmGraph platform g
= text "{" <> text "offset"
$$ nest 2 (vcat $ map (pdoc platform) blocks)
$$ text "}"
where blocks = revPostorder g
-- revPostorder has the side-effect of discarding unreachable code,
-- so pretty-printed Cmm will omit any unreachable blocks. This can
-- sometimes be confusing.
---------------------------------------------
-- Outputting CmmNode and types which it contains
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>"
pprForeignConvention :: ForeignConvention -> SDoc
pprForeignConvention (ForeignConvention c args res ret) =
doubleQuotes (ppr c) <+> text "arg hints: " <+> ppr args <+> text " result hints: " <+> ppr res <+> ppr ret
pprReturnInfo :: CmmReturnInfo -> SDoc
pprReturnInfo CmmMayReturn = empty
pprReturnInfo CmmNeverReturns = text "never returns"
pprForeignTarget :: Platform -> ForeignTarget -> SDoc
pprForeignTarget platform (ForeignTarget fn c) = ppr c <+> ppr_target fn
where
ppr_target :: CmmExpr -> SDoc
ppr_target t@(CmmLit _) = pdoc platform t
ppr_target fn' = parens (pdoc platform fn')
pprForeignTarget platform (PrimTarget op)
-- HACK: We're just using a ForeignLabel to get this printed, the label
-- might not really be foreign.
= pdoc platform
(CmmLabel (mkForeignLabel
(mkFastString (show op))
Nothing ForeignLabelInThisPackage IsFunction))
pprNode :: Platform -> CmmNode e x -> SDoc
pprNode platform node = pp_node <+> pp_debug
where
pp_node :: SDoc
pp_node = case node of
-- label:
CmmEntry id tscope ->
(sdocOption sdocSuppressUniques $ \case
True -> text "_lbl_"
False -> ppr id
)
<> colon
<+> ppUnlessOption sdocSuppressTicks (text "//" <+> ppr tscope)
-- // text
CmmComment s -> text "//" <+> ftext s
-- //tick bla<...>
CmmTick t -> ppUnlessOption sdocSuppressTicks
(text "//tick" <+> ppr t)
-- unwind reg = expr;
CmmUnwind regs ->
text "unwind "
<> commafy (map (\(r,e) -> ppr r <+> char '=' <+> pdoc platform e) regs) <> semi
-- reg = expr;
CmmAssign reg expr -> ppr reg <+> equals <+> pdoc platform expr <> semi
-- rep[lv] = expr;
CmmStore lv expr -> rep <> brackets (pdoc platform lv) <+> equals <+> pdoc platform expr <> semi
where
rep = ppr ( cmmExprType platform expr )
-- call "ccall" foo(x, y)[r1, r2];
-- ToDo ppr volatile
CmmUnsafeForeignCall target results args ->
hsep [ ppUnless (null results) $
parens (commafy $ map ppr results) <+> equals,
text "call",
pdoc platform target <> parens (commafy $ map (pdoc platform) args) <> semi]
-- goto label;
CmmBranch ident -> text "goto" <+> ppr ident <> semi
-- if (expr) goto t; else goto f;
CmmCondBranch expr t f l ->
hsep [ text "if"
, parens (pdoc platform expr)
, case l of
Nothing -> empty
Just b -> parens (text "likely:" <+> ppr b)
, text "goto"
, ppr t <> semi
, text "else goto"
, ppr f <> semi
]
CmmSwitch expr ids ->
hang (hsep [ text "switch"
, range
, if isTrivialCmmExpr expr
then pdoc platform expr
else parens (pdoc platform expr)
, text "{"
])
4 (vcat (map ppCase cases) $$ def) $$ rbrace
where
(cases, mbdef) = switchTargetsFallThrough ids
ppCase (is,l) = hsep
[ text "case"
, commafy $ map integer is
, text ": goto"
, ppr l <> semi
]
def | Just l <- mbdef = hsep
[ text "default:"
, braces (text "goto" <+> ppr l <> semi)
]
| otherwise = empty
range = brackets $ hsep [integer lo, text "..", integer hi]
where (lo,hi) = switchTargetsRange ids
CmmCall tgt k regs out res updfr_off ->
hcat [ text "call", space
, pprFun tgt, parens (interpp'SP regs), space
, returns <+>
text "args: " <> ppr out <> comma <+>
text "res: " <> ppr res <> comma <+>
text "upd: " <> ppr updfr_off
, semi ]
where pprFun f@(CmmLit _) = pdoc platform f
pprFun f = parens (pdoc platform f)
returns
| Just r <- k = text "returns to" <+> ppr r <> comma
| otherwise = empty
CmmForeignCall {tgt=t, res=rs, args=as, succ=s, ret_args=a, ret_off=u, intrbl=i} ->
hcat $ if i then [text "interruptible", space] else [] ++
[ text "foreign call", space
, pdoc platform t, text "(...)", space
, text "returns to" <+> ppr s
<+> text "args:" <+> parens (pdoc platform as)
<+> text "ress:" <+> parens (ppr rs)
, text "ret_args:" <+> ppr a
, text "ret_off:" <+> ppr u
, semi ]
pp_debug :: SDoc
pp_debug =
if not debugIsOn then empty
else case node of
CmmEntry {} -> empty -- Looks terrible with text " // CmmEntry"
CmmComment {} -> empty -- Looks also terrible with text " // CmmComment"
CmmTick {} -> empty
CmmUnwind {} -> text " // CmmUnwind"
CmmAssign {} -> text " // CmmAssign"
CmmStore {} -> text " // CmmStore"
CmmUnsafeForeignCall {} -> text " // CmmUnsafeForeignCall"
CmmBranch {} -> text " // CmmBranch"
CmmCondBranch {} -> text " // CmmCondBranch"
CmmSwitch {} -> text " // CmmSwitch"
CmmCall {} -> text " // CmmCall"
CmmForeignCall {} -> text " // CmmForeignCall"
commafy :: [SDoc] -> SDoc
commafy xs = hsep $ punctuate comma xs
|