1 ----------------------------------------------------------------------------
3 -- Pretty-printing of Cmm as (a superset of) C--
5 -- (c) The University of Glasgow 2004-2006
7 -----------------------------------------------------------------------------
10 -- This is where we walk over Cmm emitting an external representation,
11 -- suitable for parsing, in a syntax strongly reminiscent of C--. This
12 -- is the "External Core" for the Cmm layer.
14 -- As such, this should be a well-defined syntax: we want it to look nice.
15 -- Thus, we try wherever possible to use syntax defined in [1],
16 -- "The C-- Reference Manual", http://www.cminusminus.org/. We differ
17 -- slightly, in some cases. For one, we use I8 .. I64 for types, rather
18 -- than C--'s bits8 .. bits64.
20 -- We try to ensure that all information available in the abstract
21 -- syntax is reproduced, or reproducible, in the concrete syntax.
22 -- Data that is not in printed out can be reconstructed according to
23 -- conventions used in the pretty printer. There are at least two such
25 -- 1) if a value has wordRep type, the type is not appended in the
27 -- 2) MachOps that operate over wordRep type are printed in a
28 -- C-style, rather than as their internal MachRep name.
30 -- These conventions produce much more readable Cmm output.
32 -- A useful example pass over Cmm is in nativeGen/MachCodeGen.hs
36 writeCmms, pprCmms, pprCmm, pprStmt, pprExpr
39 #include "HsVersions.h"
55 pprCmms :: (Outputable info) => [GenCmm CmmStatic info CmmStmt] -> SDoc
56 pprCmms cmms = pprCode CStyle (vcat (intersperse separator $ map ppr cmms))
58 separator = space $$ ptext SLIT("-------------------") $$ space
60 writeCmms :: Handle -> [Cmm] -> IO ()
61 writeCmms handle cmms = printForC handle (pprCmms cmms)
63 -----------------------------------------------------------------------------
65 instance (Outputable info) => Outputable (GenCmm CmmStatic info CmmStmt) where
68 instance (Outputable info) => Outputable (GenCmmTop CmmStatic info CmmStmt) where
71 instance Outputable CmmBasicBlock where
74 instance Outputable CmmStmt where
77 instance Outputable CmmExpr where
80 instance Outputable CmmReg where
83 instance Outputable LocalReg where
86 instance Outputable GlobalReg where
87 ppr e = pprGlobalReg e
89 instance Outputable CmmStatic where
92 instance Outputable CmmInfo where
95 -----------------------------------------------------------------------------
97 pprCmm :: (Outputable info) => GenCmm CmmStatic info CmmStmt -> SDoc
98 pprCmm (Cmm tops) = vcat $ intersperse (text "") $ map pprTop tops
100 -- --------------------------------------------------------------------------
101 -- Top level `procedure' blocks.
103 pprTop :: (Outputable info) => GenCmmTop CmmStatic info CmmStmt -> SDoc
104 pprTop (CmmProc info lbl params blocks )
106 = vcat [ pprCLabel lbl <> parens (commafy $ map ppr params) <+> lbrace
107 , nest 8 $ lbrace <+> ppr info $$ rbrace
108 , nest 4 $ vcat (map ppr blocks)
111 -- --------------------------------------------------------------------------
112 -- We follow [1], 4.5
114 -- section "data" { ... }
116 pprTop (CmmData section ds) =
117 (hang (pprSection section <+> lbrace) 4 (vcat (map pprStatic ds)))
120 -- --------------------------------------------------------------------------
121 instance Outputable CmmSafety where
122 ppr CmmUnsafe = ptext SLIT("_unsafe_call_")
123 ppr (CmmSafe srt) = ppr srt
125 -- --------------------------------------------------------------------------
126 -- Info tables. The current pretty printer needs refinement
127 -- but will work for now.
129 -- For ideas on how to refine it, they used to be printed in the
130 -- style of C--'s 'stackdata' declaration, just inside the proc body,
131 -- and were labelled with the procedure name ++ "_info".
132 pprInfo (CmmInfo gc_target update_frame CmmNonInfoTable) =
133 vcat [{-ptext SLIT("gc_target: ") <>
134 maybe (ptext SLIT("<none>")) pprBlockId gc_target,-}
135 ptext SLIT("update_frame: ") <>
136 maybe (ptext SLIT("<none>")) pprUpdateFrame update_frame]
137 pprInfo (CmmInfo gc_target update_frame
138 (CmmInfoTable (ProfilingInfo closure_type closure_desc) tag info)) =
139 vcat [{-ptext SLIT("gc_target: ") <>
140 maybe (ptext SLIT("<none>")) pprBlockId gc_target,-}
141 ptext SLIT("update_frame: ") <>
142 maybe (ptext SLIT("<none>")) pprUpdateFrame update_frame,
143 ptext SLIT("type: ") <> pprLit closure_type,
144 ptext SLIT("desc: ") <> pprLit closure_desc,
145 ptext SLIT("tag: ") <> integer (toInteger tag),
148 pprTypeInfo (ConstrInfo layout constr descr) =
149 vcat [ptext SLIT("ptrs: ") <> integer (toInteger (fst layout)),
150 ptext SLIT("nptrs: ") <> integer (toInteger (snd layout)),
151 ptext SLIT("constructor: ") <> integer (toInteger constr),
153 pprTypeInfo (FunInfo layout srt fun_type arity args slow_entry) =
154 vcat [ptext SLIT("ptrs: ") <> integer (toInteger (fst layout)),
155 ptext SLIT("nptrs: ") <> integer (toInteger (snd layout)),
156 ptext SLIT("srt: ") <> ppr srt,
157 ptext SLIT("fun_type: ") <> integer (toInteger fun_type),
158 ptext SLIT("arity: ") <> integer (toInteger arity),
159 --ptext SLIT("args: ") <> ppr args, -- TODO: needs to be printed
160 ptext SLIT("slow: ") <> pprLit slow_entry
162 pprTypeInfo (ThunkInfo layout srt) =
163 vcat [ptext SLIT("ptrs: ") <> integer (toInteger (fst layout)),
164 ptext SLIT("nptrs: ") <> integer (toInteger (snd layout)),
165 ptext SLIT("srt: ") <> ppr srt]
166 pprTypeInfo (ThunkSelectorInfo offset srt) =
167 vcat [ptext SLIT("ptrs: ") <> integer (toInteger offset),
168 ptext SLIT("srt: ") <> ppr srt]
169 pprTypeInfo (ContInfo stack srt) =
170 vcat [ptext SLIT("stack: ") <> ppr stack,
171 ptext SLIT("srt: ") <> ppr srt]
173 pprUpdateFrame :: UpdateFrame -> SDoc
174 pprUpdateFrame (UpdateFrame expr args) =
175 hcat [ ptext SLIT("jump")
177 , if isTrivialCmmExpr expr
180 CmmLoad (CmmReg _) _ -> pprExpr expr
181 _ -> parens (pprExpr expr)
183 , parens ( commafy $ map ppr args ) ]
186 -- --------------------------------------------------------------------------
187 -- Basic blocks look like assembly blocks.
188 -- lbl: stmt ; stmt ; ..
189 pprBBlock :: CmmBasicBlock -> SDoc
190 pprBBlock (BasicBlock ident stmts) =
191 hang (pprBlockId ident <> colon) 4 (vcat (map ppr stmts))
193 -- --------------------------------------------------------------------------
194 -- Statements. C-- usually, exceptions to this should be obvious.
196 pprStmt :: CmmStmt -> SDoc
197 pprStmt stmt = case stmt of
203 CmmComment s -> text "//" <+> ftext s
206 CmmAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi
209 CmmStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi
211 rep = ppr ( cmmExprRep expr )
213 -- call "ccall" foo(x, y)[r1, r2];
215 CmmCall (CmmCallee fn cconv) results args safety ret ->
216 hcat [ if null results
218 else parens (commafy $ map ppr results) <>
220 ptext SLIT("call"), space,
221 doubleQuotes(ppr cconv), space,
222 target fn, parens ( commafy $ map ppr args ),
223 brackets (ppr safety),
224 case ret of CmmMayReturn -> empty
225 CmmNeverReturns -> ptext SLIT(" never returns"),
228 target (CmmLit lit) = pprLit lit
229 target fn' = parens (ppr fn')
231 CmmCall (CmmPrim op) results args safety ret ->
232 pprStmt (CmmCall (CmmCallee (CmmLit lbl) CCallConv)
233 results args safety ret)
235 lbl = CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False)
237 CmmBranch ident -> genBranch ident
238 CmmCondBranch expr ident -> genCondBranch expr ident
239 CmmJump expr params -> genJump expr params
240 CmmReturn params -> genReturn params
241 CmmSwitch arg ids -> genSwitch arg ids
243 -- --------------------------------------------------------------------------
244 -- goto local label. [1], section 6.6
248 genBranch :: BlockId -> SDoc
250 ptext SLIT("goto") <+> pprBlockId ident <> semi
252 -- --------------------------------------------------------------------------
253 -- Conditional. [1], section 6.4
255 -- if (expr) { goto lbl; }
257 genCondBranch :: CmmExpr -> BlockId -> SDoc
258 genCondBranch expr ident =
259 hsep [ ptext SLIT("if")
262 , pprBlockId ident <> semi ]
264 -- --------------------------------------------------------------------------
265 -- A tail call. [1], Section 6.9
267 -- jump foo(a, b, c);
269 genJump :: CmmExpr -> [(CmmExpr, MachHint)] -> SDoc
272 hcat [ ptext SLIT("jump")
274 , if isTrivialCmmExpr expr
277 CmmLoad (CmmReg _) _ -> pprExpr expr
278 _ -> parens (pprExpr expr)
280 , parens ( commafy $ map ppr args )
283 -- --------------------------------------------------------------------------
284 -- Return from a function. [1], Section 6.8.2 of version 1.128
288 genReturn :: [(CmmExpr, MachHint)] -> SDoc
291 hcat [ ptext SLIT("return")
293 , parens ( commafy $ map ppr args )
296 -- --------------------------------------------------------------------------
297 -- Tabled jump to local label
299 -- The syntax is from [1], section 6.5
301 -- switch [0 .. n] (expr) { case ... ; }
303 genSwitch :: CmmExpr -> [Maybe BlockId] -> SDoc
304 genSwitch expr maybe_ids
306 = let pairs = groupBy snds (zip [0 .. ] maybe_ids )
308 in hang (hcat [ ptext SLIT("switch [0 .. ")
309 , int (length maybe_ids - 1)
311 , if isTrivialCmmExpr expr
313 else parens (pprExpr expr)
316 4 (vcat ( map caseify pairs )) $$ rbrace
319 snds a b = (snd a) == (snd b)
321 caseify :: [(Int,Maybe BlockId)] -> SDoc
322 caseify ixs@((i,Nothing):_)
323 = ptext SLIT("/* impossible: ") <> hcat (intersperse comma (map (int.fst) ixs))
326 = let (is,ids) = unzip as
327 in hsep [ ptext SLIT("case")
328 , hcat (punctuate comma (map int is))
329 , ptext SLIT(": goto")
330 , pprBlockId (head [ id | Just id <- ids]) <> semi ]
332 -- --------------------------------------------------------------------------
336 pprExpr :: CmmExpr -> SDoc
340 pprExpr (CmmMachOp (MO_Add rep)
341 [CmmReg reg, CmmLit (CmmInt (fromIntegral i) rep)])
342 where rep = cmmRegRep reg
343 CmmLit lit -> pprLit lit
346 -- Here's the precedence table from CmmParse.y:
347 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
356 -- We just cope with the common operators for now, the rest will get
357 -- a default conservative behaviour.
359 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
360 pprExpr1 (CmmMachOp op [x,y]) | Just doc <- infixMachOp1 op
361 = pprExpr7 x <+> doc <+> pprExpr7 y
362 pprExpr1 e = pprExpr7 e
364 infixMachOp1 (MO_Eq _) = Just (ptext SLIT("=="))
365 infixMachOp1 (MO_Ne _) = Just (ptext SLIT("!="))
366 infixMachOp1 (MO_Shl _) = Just (ptext SLIT("<<"))
367 infixMachOp1 (MO_U_Shr _) = Just (ptext SLIT(">>"))
368 infixMachOp1 (MO_U_Ge _) = Just (ptext SLIT(">="))
369 infixMachOp1 (MO_U_Le _) = Just (ptext SLIT("<="))
370 infixMachOp1 (MO_U_Gt _) = Just (char '>')
371 infixMachOp1 (MO_U_Lt _) = Just (char '<')
372 infixMachOp1 _ = Nothing
375 pprExpr7 (CmmMachOp (MO_Add rep1) [x, CmmLit (CmmInt i rep2)]) | i < 0
376 = pprExpr7 (CmmMachOp (MO_Sub rep1) [x, CmmLit (CmmInt (negate i) rep2)])
377 pprExpr7 (CmmMachOp op [x,y]) | Just doc <- infixMachOp7 op
378 = pprExpr7 x <+> doc <+> pprExpr8 y
379 pprExpr7 e = pprExpr8 e
381 infixMachOp7 (MO_Add _) = Just (char '+')
382 infixMachOp7 (MO_Sub _) = Just (char '-')
383 infixMachOp7 _ = Nothing
386 pprExpr8 (CmmMachOp op [x,y]) | Just doc <- infixMachOp8 op
387 = pprExpr8 x <+> doc <+> pprExpr9 y
388 pprExpr8 e = pprExpr9 e
390 infixMachOp8 (MO_U_Quot _) = Just (char '/')
391 infixMachOp8 (MO_Mul _) = Just (char '*')
392 infixMachOp8 (MO_U_Rem _) = Just (char '%')
393 infixMachOp8 _ = Nothing
395 pprExpr9 :: CmmExpr -> SDoc
398 CmmLit lit -> pprLit1 lit
399 CmmLoad expr rep -> ppr rep <> brackets( ppr expr )
400 CmmReg reg -> ppr reg
401 CmmRegOff reg off -> parens (ppr reg <+> char '+' <+> int off)
402 CmmMachOp mop args -> genMachOp mop args
404 genMachOp :: MachOp -> [CmmExpr] -> SDoc
406 | Just doc <- infixMachOp mop = case args of
408 [x,y] -> pprExpr9 x <+> doc <+> pprExpr9 y
411 [x] -> doc <> pprExpr9 x
413 _ -> pprTrace "PprCmm.genMachOp: machop with strange number of args"
415 parens (hcat $ punctuate comma (map pprExpr args)))
418 | isJust (infixMachOp1 mop)
419 || isJust (infixMachOp7 mop)
420 || isJust (infixMachOp8 mop) = parens (pprExpr (CmmMachOp mop args))
422 | otherwise = char '%' <> ppr_op <> parens (commafy (map pprExpr args))
423 where ppr_op = text (map (\c -> if c == ' ' then '_' else c)
425 -- replace spaces in (show mop) with underscores,
428 -- Unsigned ops on the word size of the machine get nice symbols.
429 -- All else get dumped in their ugly format.
431 infixMachOp :: MachOp -> Maybe SDoc
434 MO_And _ -> Just $ char '&'
435 MO_Or _ -> Just $ char '|'
436 MO_Xor _ -> Just $ char '^'
437 MO_Not _ -> Just $ char '~'
438 MO_S_Neg _ -> Just $ char '-' -- there is no unsigned neg :)
441 -- --------------------------------------------------------------------------
443 -- To minimise line noise we adopt the convention that if the literal
444 -- has the natural machine word size, we do not append the type
446 pprLit :: CmmLit -> SDoc
447 pprLit lit = case lit of
449 hcat [ (if i < 0 then parens else id)(integer i)
452 else space <> dcolon <+> ppr rep) ]
454 CmmFloat f rep -> hsep [ rational f, dcolon, ppr rep ]
455 CmmLabel clbl -> pprCLabel clbl
456 CmmLabelOff clbl i -> pprCLabel clbl <> ppr_offset i
457 CmmLabelDiffOff clbl1 clbl2 i -> pprCLabel clbl1 <> char '-'
458 <> pprCLabel clbl2 <> ppr_offset i
460 pprLit1 lit@(CmmLabelOff clbl i) = parens (pprLit lit)
461 pprLit1 lit = pprLit lit
463 ppr_offset :: Int -> SDoc
466 | i>=0 = char '+' <> int i
467 | otherwise = char '-' <> int (-i)
469 -- --------------------------------------------------------------------------
471 -- Strings are printed as C strings, and we print them as I8[],
474 pprStatic :: CmmStatic -> SDoc
475 pprStatic s = case s of
476 CmmStaticLit lit -> nest 4 $ ptext SLIT("const") <+> pprLit lit <> semi
477 CmmUninitialised i -> nest 4 $ text "I8" <> brackets (int i)
478 CmmAlign i -> nest 4 $ text "align" <+> int i
479 CmmDataLabel clbl -> pprCLabel clbl <> colon
480 CmmString s' -> nest 4 $ text "I8[]" <+> text (show s')
482 -- --------------------------------------------------------------------------
483 -- Registers, whether local (temps) or global
485 pprReg :: CmmReg -> SDoc
488 CmmLocal local -> pprLocalReg local
489 CmmGlobal global -> pprGlobalReg global
492 -- We only print the type of the local reg if it isn't wordRep
494 pprLocalReg :: LocalReg -> SDoc
495 pprLocalReg (LocalReg uniq rep follow)
496 = hcat [ char '_', ppr uniq, ty ] where
497 ty = if rep == wordRep && follow == KindNonPtr
499 else dcolon <> ptr <> ppr rep
500 ptr = if follow == KindNonPtr
502 else doubleQuotes (text "ptr")
504 -- needs to be kept in syn with Cmm.hs.GlobalReg
506 pprGlobalReg :: GlobalReg -> SDoc
509 VanillaReg n -> char 'R' <> int n
510 FloatReg n -> char 'F' <> int n
511 DoubleReg n -> char 'D' <> int n
512 LongReg n -> char 'L' <> int n
513 Sp -> ptext SLIT("Sp")
514 SpLim -> ptext SLIT("SpLim")
515 Hp -> ptext SLIT("Hp")
516 HpLim -> ptext SLIT("HpLim")
517 CurrentTSO -> ptext SLIT("CurrentTSO")
518 CurrentNursery -> ptext SLIT("CurrentNursery")
519 HpAlloc -> ptext SLIT("HpAlloc")
520 GCEnter1 -> ptext SLIT("stg_gc_enter_1")
521 GCFun -> ptext SLIT("stg_gc_fun")
522 BaseReg -> ptext SLIT("BaseReg")
523 PicBaseReg -> ptext SLIT("PicBaseReg")
525 -- --------------------------------------------------------------------------
528 pprSection :: Section -> SDoc
529 pprSection s = case s of
530 Text -> section <+> doubleQuotes (ptext SLIT("text"))
531 Data -> section <+> doubleQuotes (ptext SLIT("data"))
532 ReadOnlyData -> section <+> doubleQuotes (ptext SLIT("readonly"))
533 RelocatableReadOnlyData
534 -> section <+> doubleQuotes (ptext SLIT("relreadonly"))
535 UninitialisedData -> section <+> doubleQuotes (ptext SLIT("uninitialised"))
536 OtherSection s' -> section <+> doubleQuotes (text s')
538 section = ptext SLIT("section")
540 -- --------------------------------------------------------------------------
543 pprBlockId :: BlockId -> SDoc
544 pprBlockId b = ppr $ getUnique b
546 -----------------------------------------------------------------------------
548 commafy :: [SDoc] -> SDoc
549 commafy xs = hsep $ punctuate comma xs