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, pprSection, pprStatic, pprLit
55 pprCmms :: (Outputable info, Outputable g) => [GenCmm CmmStatic info g] -> 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 d, Outputable info, Outputable g)
66 => Outputable (GenCmm d info g) where
69 instance (Outputable d, Outputable info, Outputable i)
70 => Outputable (GenCmmTop d info i) where
73 instance (Outputable instr) => Outputable (ListGraph instr) where
74 ppr (ListGraph blocks) = vcat (map ppr blocks)
76 instance (Outputable instr) => Outputable (GenBasicBlock instr) where
79 instance Outputable CmmStmt where
82 instance Outputable CmmExpr where
85 instance Outputable CmmReg where
88 instance Outputable CmmLit where
91 instance Outputable LocalReg where
94 instance Outputable Area where
97 instance Outputable GlobalReg where
98 ppr e = pprGlobalReg e
100 instance Outputable CmmStatic where
103 instance Outputable CmmInfo where
108 -----------------------------------------------------------------------------
110 pprCmm :: (Outputable d, Outputable info, Outputable g) => GenCmm d info g -> SDoc
111 pprCmm (Cmm tops) = vcat $ intersperse (text "") $ map pprTop tops
113 -- --------------------------------------------------------------------------
114 -- Top level `procedure' blocks.
116 pprTop :: (Outputable d, Outputable info, Outputable i)
117 => GenCmmTop d info i -> SDoc
119 pprTop (CmmProc info lbl params graph )
121 = vcat [ pprCLabel lbl <> parens (commafy $ map ppr params) <+> lbrace
122 , nest 8 $ lbrace <+> ppr info $$ rbrace
126 -- --------------------------------------------------------------------------
127 -- We follow [1], 4.5
129 -- section "data" { ... }
131 pprTop (CmmData section ds) =
132 (hang (pprSection section <+> lbrace) 4 (vcat (map ppr ds)))
135 -- --------------------------------------------------------------------------
136 instance Outputable CmmSafety where
137 ppr CmmUnsafe = ptext (sLit "_unsafe_call_")
138 ppr (CmmSafe srt) = ppr srt
140 -- --------------------------------------------------------------------------
141 -- Info tables. The current pretty printer needs refinement
142 -- but will work for now.
144 -- For ideas on how to refine it, they used to be printed in the
145 -- style of C--'s 'stackdata' declaration, just inside the proc body,
146 -- and were labelled with the procedure name ++ "_info".
147 pprInfo :: CmmInfo -> SDoc
148 pprInfo (CmmInfo _gc_target update_frame CmmNonInfoTable) =
149 vcat [{-ptext (sLit "gc_target: ") <>
150 maybe (ptext (sLit "<none>")) pprBlockId gc_target,-}
151 ptext (sLit "update_frame: ") <>
152 maybe (ptext (sLit "<none>")) pprUpdateFrame update_frame]
153 pprInfo (CmmInfo _gc_target update_frame
154 (CmmInfoTable (ProfilingInfo closure_type closure_desc) tag info)) =
155 vcat [{-ptext (sLit "gc_target: ") <>
156 maybe (ptext (sLit "<none>")) pprBlockId gc_target,-}
157 ptext (sLit "update_frame: ") <>
158 maybe (ptext (sLit "<none>")) pprUpdateFrame update_frame,
159 ptext (sLit "type: ") <> pprLit closure_type,
160 ptext (sLit "desc: ") <> pprLit closure_desc,
161 ptext (sLit "tag: ") <> integer (toInteger tag),
164 pprTypeInfo :: ClosureTypeInfo -> SDoc
165 pprTypeInfo (ConstrInfo layout constr descr) =
166 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
167 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
168 ptext (sLit "constructor: ") <> integer (toInteger constr),
170 pprTypeInfo (FunInfo layout srt fun_type arity _args slow_entry) =
171 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
172 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
173 ptext (sLit "srt: ") <> ppr srt,
174 ptext (sLit "fun_type: ") <> integer (toInteger fun_type),
175 ptext (sLit "arity: ") <> integer (toInteger arity),
176 --ptext (sLit "args: ") <> ppr args, -- TODO: needs to be printed
177 ptext (sLit "slow: ") <> pprLit slow_entry
179 pprTypeInfo (ThunkInfo layout srt) =
180 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
181 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
182 ptext (sLit "srt: ") <> ppr srt]
183 pprTypeInfo (ThunkSelectorInfo offset srt) =
184 vcat [ptext (sLit "ptrs: ") <> integer (toInteger offset),
185 ptext (sLit "srt: ") <> ppr srt]
186 pprTypeInfo (ContInfo stack srt) =
187 vcat [ptext (sLit "stack: ") <> ppr stack,
188 ptext (sLit "srt: ") <> ppr srt]
190 pprUpdateFrame :: UpdateFrame -> SDoc
191 pprUpdateFrame (UpdateFrame expr args) =
192 hcat [ ptext (sLit "jump")
194 , if isTrivialCmmExpr expr
197 CmmLoad (CmmReg _) _ -> pprExpr expr
198 _ -> parens (pprExpr expr)
200 , parens ( commafy $ map ppr args ) ]
203 -- --------------------------------------------------------------------------
204 -- Basic blocks look like assembly blocks.
205 -- lbl: stmt ; stmt ; ..
206 pprBBlock :: Outputable stmt => GenBasicBlock stmt -> SDoc
207 pprBBlock (BasicBlock ident stmts) =
208 hang (pprBlockId ident <> colon) 4 (vcat (map ppr stmts))
210 -- --------------------------------------------------------------------------
211 -- Statements. C-- usually, exceptions to this should be obvious.
213 pprStmt :: CmmStmt -> SDoc
214 pprStmt stmt = case stmt of
220 CmmComment s -> text "//" <+> ftext s
223 CmmAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi
226 CmmStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi
228 rep = ppr ( cmmExprRep expr )
230 -- call "ccall" foo(x, y)[r1, r2];
232 CmmCall (CmmCallee fn cconv) results args safety ret ->
233 hcat [ if null results
235 else parens (commafy $ map ppr results) <>
237 ptext (sLit "foreign"), space,
238 doubleQuotes(ppr cconv), space,
239 target fn, parens ( commafy $ map ppr args ),
240 brackets (ppr safety),
241 case ret of CmmMayReturn -> empty
242 CmmNeverReturns -> ptext (sLit " never returns"),
245 ---- With the following three functions, I was going somewhere
246 ---- useful, but I don't remember where. Probably making
247 ---- emitted Cmm output look better. ---NR, 2 May 2008
248 _pp_lhs | null results = empty
249 | otherwise = commafy (map ppr_ar results) <+> equals
250 -- Don't print the hints on a native C-- call
251 ppr_ar arg = case cconv of
252 CmmCallConv -> ppr (kindlessCmm arg)
253 _ -> doubleQuotes (ppr $ cmmKind arg) <+>
254 ppr (kindlessCmm arg)
255 _pp_conv = case cconv of
257 _ -> ptext (sLit "foreign") <+> doubleQuotes (ppr cconv)
259 target (CmmLit lit) = pprLit lit
260 target fn' = parens (ppr fn')
262 CmmCall (CmmPrim op) results args safety ret ->
263 pprStmt (CmmCall (CmmCallee (CmmLit lbl) CCallConv)
264 results args safety ret)
266 lbl = CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False)
268 CmmBranch ident -> genBranch ident
269 CmmCondBranch expr ident -> genCondBranch expr ident
270 CmmJump expr params -> genJump expr params
271 CmmReturn params -> genReturn params
272 CmmSwitch arg ids -> genSwitch arg ids
274 -- --------------------------------------------------------------------------
275 -- goto local label. [1], section 6.6
279 genBranch :: BlockId -> SDoc
281 ptext (sLit "goto") <+> pprBlockId ident <> semi
283 -- --------------------------------------------------------------------------
284 -- Conditional. [1], section 6.4
286 -- if (expr) { goto lbl; }
288 genCondBranch :: CmmExpr -> BlockId -> SDoc
289 genCondBranch expr ident =
290 hsep [ ptext (sLit "if")
292 , ptext (sLit "goto")
293 , pprBlockId ident <> semi ]
295 -- --------------------------------------------------------------------------
296 -- A tail call. [1], Section 6.9
298 -- jump foo(a, b, c);
300 genJump :: CmmExpr -> [CmmKinded CmmExpr] -> SDoc
303 hcat [ ptext (sLit "jump")
305 , if isTrivialCmmExpr expr
308 CmmLoad (CmmReg _) _ -> pprExpr expr
309 _ -> parens (pprExpr expr)
311 , parens ( commafy $ map pprKinded args )
314 pprKinded :: Outputable a => (CmmKinded a) -> SDoc
315 pprKinded (CmmKinded a NoHint) = ppr a
316 pprKinded (CmmKinded a PtrHint) = quotes(text "address") <+> ppr a
317 pprKinded (CmmKinded a SignedHint) = quotes(text "signed") <+> ppr a
318 pprKinded (CmmKinded a FloatHint) = quotes(text "float") <+> ppr a
320 -- --------------------------------------------------------------------------
321 -- Return from a function. [1], Section 6.8.2 of version 1.128
325 genReturn :: [CmmKinded CmmExpr] -> SDoc
328 hcat [ ptext (sLit "return")
330 , parens ( commafy $ map ppr args )
333 -- --------------------------------------------------------------------------
334 -- Tabled jump to local label
336 -- The syntax is from [1], section 6.5
338 -- switch [0 .. n] (expr) { case ... ; }
340 genSwitch :: CmmExpr -> [Maybe BlockId] -> SDoc
341 genSwitch expr maybe_ids
343 = let pairs = groupBy snds (zip [0 .. ] maybe_ids )
345 in hang (hcat [ ptext (sLit "switch [0 .. ")
346 , int (length maybe_ids - 1)
348 , if isTrivialCmmExpr expr
350 else parens (pprExpr expr)
353 4 (vcat ( map caseify pairs )) $$ rbrace
356 snds a b = (snd a) == (snd b)
358 caseify :: [(Int,Maybe BlockId)] -> SDoc
359 caseify ixs@((_,Nothing):_)
360 = ptext (sLit "/* impossible: ") <> hcat (intersperse comma (map (int.fst) ixs))
361 <> ptext (sLit " */")
363 = let (is,ids) = unzip as
364 in hsep [ ptext (sLit "case")
365 , hcat (punctuate comma (map int is))
366 , ptext (sLit ": goto")
367 , pprBlockId (head [ id | Just id <- ids]) <> semi ]
369 -- --------------------------------------------------------------------------
373 pprExpr :: CmmExpr -> SDoc
377 pprExpr (CmmMachOp (MO_Add rep)
378 [CmmReg reg, CmmLit (CmmInt (fromIntegral i) rep)])
379 where rep = cmmRegRep reg
380 CmmLit lit -> pprLit lit
383 -- Here's the precedence table from CmmParse.y:
384 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
393 -- We just cope with the common operators for now, the rest will get
394 -- a default conservative behaviour.
396 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
397 pprExpr1, pprExpr7, pprExpr8 :: CmmExpr -> SDoc
398 pprExpr1 (CmmMachOp op [x,y]) | Just doc <- infixMachOp1 op
399 = pprExpr7 x <+> doc <+> pprExpr7 y
400 pprExpr1 e = pprExpr7 e
402 infixMachOp1, infixMachOp7, infixMachOp8 :: MachOp -> Maybe SDoc
404 infixMachOp1 (MO_Eq _) = Just (ptext (sLit "=="))
405 infixMachOp1 (MO_Ne _) = Just (ptext (sLit "!="))
406 infixMachOp1 (MO_Shl _) = Just (ptext (sLit "<<"))
407 infixMachOp1 (MO_U_Shr _) = Just (ptext (sLit ">>"))
408 infixMachOp1 (MO_U_Ge _) = Just (ptext (sLit ">="))
409 infixMachOp1 (MO_U_Le _) = Just (ptext (sLit "<="))
410 infixMachOp1 (MO_U_Gt _) = Just (char '>')
411 infixMachOp1 (MO_U_Lt _) = Just (char '<')
412 infixMachOp1 _ = Nothing
415 pprExpr7 (CmmMachOp (MO_Add rep1) [x, CmmLit (CmmInt i rep2)]) | i < 0
416 = pprExpr7 (CmmMachOp (MO_Sub rep1) [x, CmmLit (CmmInt (negate i) rep2)])
417 pprExpr7 (CmmMachOp op [x,y]) | Just doc <- infixMachOp7 op
418 = pprExpr7 x <+> doc <+> pprExpr8 y
419 pprExpr7 e = pprExpr8 e
421 infixMachOp7 (MO_Add _) = Just (char '+')
422 infixMachOp7 (MO_Sub _) = Just (char '-')
423 infixMachOp7 _ = Nothing
426 pprExpr8 (CmmMachOp op [x,y]) | Just doc <- infixMachOp8 op
427 = pprExpr8 x <+> doc <+> pprExpr9 y
428 pprExpr8 e = pprExpr9 e
430 infixMachOp8 (MO_U_Quot _) = Just (char '/')
431 infixMachOp8 (MO_Mul _) = Just (char '*')
432 infixMachOp8 (MO_U_Rem _) = Just (char '%')
433 infixMachOp8 _ = Nothing
435 pprExpr9 :: CmmExpr -> SDoc
438 CmmLit lit -> pprLit1 lit
439 CmmLoad expr rep -> ppr rep <> brackets( ppr expr )
440 CmmReg reg -> ppr reg
441 CmmRegOff reg off -> parens (ppr reg <+> char '+' <+> int off)
442 CmmStackSlot a off -> parens (ppr a <+> char '+' <+> int off)
443 CmmMachOp mop args -> genMachOp mop args
445 genMachOp :: MachOp -> [CmmExpr] -> SDoc
447 | Just doc <- infixMachOp mop = case args of
449 [x,y] -> pprExpr9 x <+> doc <+> pprExpr9 y
452 [x] -> doc <> pprExpr9 x
454 _ -> pprTrace "PprCmm.genMachOp: machop with strange number of args"
456 parens (hcat $ punctuate comma (map pprExpr args)))
459 | isJust (infixMachOp1 mop)
460 || isJust (infixMachOp7 mop)
461 || isJust (infixMachOp8 mop) = parens (pprExpr (CmmMachOp mop args))
463 | otherwise = char '%' <> ppr_op <> parens (commafy (map pprExpr args))
464 where ppr_op = text (map (\c -> if c == ' ' then '_' else c)
466 -- replace spaces in (show mop) with underscores,
469 -- Unsigned ops on the word size of the machine get nice symbols.
470 -- All else get dumped in their ugly format.
472 infixMachOp :: MachOp -> Maybe SDoc
475 MO_And _ -> Just $ char '&'
476 MO_Or _ -> Just $ char '|'
477 MO_Xor _ -> Just $ char '^'
478 MO_Not _ -> Just $ char '~'
479 MO_S_Neg _ -> Just $ char '-' -- there is no unsigned neg :)
482 -- --------------------------------------------------------------------------
484 -- To minimise line noise we adopt the convention that if the literal
485 -- has the natural machine word size, we do not append the type
487 pprLit :: CmmLit -> SDoc
488 pprLit lit = case lit of
490 hcat [ (if i < 0 then parens else id)(integer i)
493 else space <> dcolon <+> ppr rep) ]
495 CmmFloat f rep -> hsep [ rational f, dcolon, ppr rep ]
496 CmmLabel clbl -> pprCLabel clbl
497 CmmLabelOff clbl i -> pprCLabel clbl <> ppr_offset i
498 CmmLabelDiffOff clbl1 clbl2 i -> pprCLabel clbl1 <> char '-'
499 <> pprCLabel clbl2 <> ppr_offset i
501 pprLit1 :: CmmLit -> SDoc
502 pprLit1 lit@(CmmLabelOff {}) = parens (pprLit lit)
503 pprLit1 lit = pprLit lit
505 ppr_offset :: Int -> SDoc
508 | i>=0 = char '+' <> int i
509 | otherwise = char '-' <> int (-i)
511 -- --------------------------------------------------------------------------
513 -- Strings are printed as C strings, and we print them as I8[],
516 pprStatic :: CmmStatic -> SDoc
517 pprStatic s = case s of
518 CmmStaticLit lit -> nest 4 $ ptext (sLit "const") <+> pprLit lit <> semi
519 CmmUninitialised i -> nest 4 $ text "I8" <> brackets (int i)
520 CmmAlign i -> nest 4 $ text "align" <+> int i
521 CmmDataLabel clbl -> pprCLabel clbl <> colon
522 CmmString s' -> nest 4 $ text "I8[]" <+> text (show s')
524 -- --------------------------------------------------------------------------
525 -- Registers, whether local (temps) or global
527 pprReg :: CmmReg -> SDoc
530 CmmLocal local -> pprLocalReg local
531 CmmGlobal global -> pprGlobalReg global
534 -- We only print the type of the local reg if it isn't wordRep
536 pprLocalReg :: LocalReg -> SDoc
537 pprLocalReg (LocalReg uniq rep follow)
538 = hcat [ char '_', ppr uniq, ty ] where
539 ty = if rep == wordRep && follow == GCKindNonPtr
541 else dcolon <> ptr <> ppr rep
542 ptr = if follow == GCKindNonPtr
544 else doubleQuotes (text "ptr")
547 pprArea :: Area -> SDoc
548 pprArea (RegSlot r) = hcat [ text "slot<", ppr r, text ">" ]
549 pprArea (CallArea id n n') =
550 hcat [ text "callslot<", ppr id, char '+', ppr n, char '/', ppr n', text ">" ]
552 -- needs to be kept in syn with Cmm.hs.GlobalReg
554 pprGlobalReg :: GlobalReg -> SDoc
557 VanillaReg n -> char 'R' <> int n
558 FloatReg n -> char 'F' <> int n
559 DoubleReg n -> char 'D' <> int n
560 LongReg n -> char 'L' <> int n
561 Sp -> ptext (sLit "Sp")
562 SpLim -> ptext (sLit "SpLim")
563 Hp -> ptext (sLit "Hp")
564 HpLim -> ptext (sLit "HpLim")
565 CurrentTSO -> ptext (sLit "CurrentTSO")
566 CurrentNursery -> ptext (sLit "CurrentNursery")
567 HpAlloc -> ptext (sLit "HpAlloc")
568 GCEnter1 -> ptext (sLit "stg_gc_enter_1")
569 GCFun -> ptext (sLit "stg_gc_fun")
570 BaseReg -> ptext (sLit "BaseReg")
571 PicBaseReg -> ptext (sLit "PicBaseReg")
573 -- --------------------------------------------------------------------------
576 pprSection :: Section -> SDoc
577 pprSection s = case s of
578 Text -> section <+> doubleQuotes (ptext (sLit "text"))
579 Data -> section <+> doubleQuotes (ptext (sLit "data"))
580 ReadOnlyData -> section <+> doubleQuotes (ptext (sLit "readonly"))
581 ReadOnlyData16 -> section <+> doubleQuotes (ptext (sLit "readonly16"))
582 RelocatableReadOnlyData
583 -> section <+> doubleQuotes (ptext (sLit "relreadonly"))
584 UninitialisedData -> section <+> doubleQuotes (ptext (sLit "uninitialised"))
585 OtherSection s' -> section <+> doubleQuotes (text s')
587 section = ptext (sLit "section")
589 -- --------------------------------------------------------------------------
592 pprBlockId :: BlockId -> SDoc
593 pprBlockId b = ppr $ getUnique b
595 -----------------------------------------------------------------------------
597 commafy :: [SDoc] -> SDoc
598 commafy xs = fsep $ punctuate comma xs