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 GlobalReg where
95 ppr e = pprGlobalReg e
97 instance Outputable CmmStatic where
100 instance Outputable CmmInfo where
105 -----------------------------------------------------------------------------
107 pprCmm :: (Outputable d, Outputable info, Outputable g) => GenCmm d info g -> SDoc
108 pprCmm (Cmm tops) = vcat $ intersperse (text "") $ map pprTop tops
110 -- --------------------------------------------------------------------------
111 -- Top level `procedure' blocks.
113 pprTop :: (Outputable d, Outputable info, Outputable i)
114 => GenCmmTop d info i -> SDoc
116 pprTop (CmmProc info lbl params graph )
118 = vcat [ pprCLabel lbl <> parens (commafy $ map ppr params) <+> lbrace
119 , nest 8 $ lbrace <+> ppr info $$ rbrace
123 -- --------------------------------------------------------------------------
124 -- We follow [1], 4.5
126 -- section "data" { ... }
128 pprTop (CmmData section ds) =
129 (hang (pprSection section <+> lbrace) 4 (vcat (map ppr ds)))
132 -- --------------------------------------------------------------------------
133 instance Outputable CmmSafety where
134 ppr CmmUnsafe = ptext (sLit "_unsafe_call_")
135 ppr (CmmSafe srt) = ppr srt
137 -- --------------------------------------------------------------------------
138 -- Info tables. The current pretty printer needs refinement
139 -- but will work for now.
141 -- For ideas on how to refine it, they used to be printed in the
142 -- style of C--'s 'stackdata' declaration, just inside the proc body,
143 -- and were labelled with the procedure name ++ "_info".
144 pprInfo :: CmmInfo -> SDoc
145 pprInfo (CmmInfo _gc_target update_frame CmmNonInfoTable) =
146 vcat [{-ptext (sLit "gc_target: ") <>
147 maybe (ptext (sLit "<none>")) pprBlockId gc_target,-}
148 ptext (sLit "update_frame: ") <>
149 maybe (ptext (sLit "<none>")) pprUpdateFrame update_frame]
150 pprInfo (CmmInfo _gc_target update_frame
151 (CmmInfoTable (ProfilingInfo closure_type closure_desc) tag info)) =
152 vcat [{-ptext (sLit "gc_target: ") <>
153 maybe (ptext (sLit "<none>")) pprBlockId gc_target,-}
154 ptext (sLit "update_frame: ") <>
155 maybe (ptext (sLit "<none>")) pprUpdateFrame update_frame,
156 ptext (sLit "type: ") <> pprLit closure_type,
157 ptext (sLit "desc: ") <> pprLit closure_desc,
158 ptext (sLit "tag: ") <> integer (toInteger tag),
161 pprTypeInfo :: ClosureTypeInfo -> SDoc
162 pprTypeInfo (ConstrInfo layout constr descr) =
163 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
164 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
165 ptext (sLit "constructor: ") <> integer (toInteger constr),
167 pprTypeInfo (FunInfo layout srt fun_type arity _args slow_entry) =
168 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
169 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
170 ptext (sLit "srt: ") <> ppr srt,
171 ptext (sLit "fun_type: ") <> integer (toInteger fun_type),
172 ptext (sLit "arity: ") <> integer (toInteger arity),
173 --ptext (sLit "args: ") <> ppr args, -- TODO: needs to be printed
174 ptext (sLit "slow: ") <> pprLit slow_entry
176 pprTypeInfo (ThunkInfo layout srt) =
177 vcat [ptext (sLit "ptrs: ") <> integer (toInteger (fst layout)),
178 ptext (sLit "nptrs: ") <> integer (toInteger (snd layout)),
179 ptext (sLit "srt: ") <> ppr srt]
180 pprTypeInfo (ThunkSelectorInfo offset srt) =
181 vcat [ptext (sLit "ptrs: ") <> integer (toInteger offset),
182 ptext (sLit "srt: ") <> ppr srt]
183 pprTypeInfo (ContInfo stack srt) =
184 vcat [ptext (sLit "stack: ") <> ppr stack,
185 ptext (sLit "srt: ") <> ppr srt]
187 pprUpdateFrame :: UpdateFrame -> SDoc
188 pprUpdateFrame (UpdateFrame expr args) =
189 hcat [ ptext (sLit "jump")
191 , if isTrivialCmmExpr expr
194 CmmLoad (CmmReg _) _ -> pprExpr expr
195 _ -> parens (pprExpr expr)
197 , parens ( commafy $ map ppr args ) ]
200 -- --------------------------------------------------------------------------
201 -- Basic blocks look like assembly blocks.
202 -- lbl: stmt ; stmt ; ..
203 pprBBlock :: Outputable stmt => GenBasicBlock stmt -> SDoc
204 pprBBlock (BasicBlock ident stmts) =
205 hang (pprBlockId ident <> colon) 4 (vcat (map ppr stmts))
207 -- --------------------------------------------------------------------------
208 -- Statements. C-- usually, exceptions to this should be obvious.
210 pprStmt :: CmmStmt -> SDoc
211 pprStmt stmt = case stmt of
217 CmmComment s -> text "//" <+> ftext s
220 CmmAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi
223 CmmStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi
225 rep = ppr ( cmmExprRep expr )
227 -- call "ccall" foo(x, y)[r1, r2];
229 CmmCall (CmmCallee fn cconv) results args safety ret ->
230 hcat [ if null results
232 else parens (commafy $ map ppr results) <>
234 ptext (sLit "foreign"), space,
235 doubleQuotes(ppr cconv), space,
236 target fn, parens ( commafy $ map ppr args ),
237 brackets (ppr safety),
238 case ret of CmmMayReturn -> empty
239 CmmNeverReturns -> ptext (sLit " never returns"),
242 ---- With the following three functions, I was going somewhere
243 ---- useful, but I don't remember where. Probably making
244 ---- emitted Cmm output look better. ---NR, 2 May 2008
245 _pp_lhs | null results = empty
246 | otherwise = commafy (map ppr_ar results) <+> equals
247 -- Don't print the hints on a native C-- call
248 ppr_ar arg = case cconv of
249 CmmCallConv -> ppr (kindlessCmm arg)
250 _ -> doubleQuotes (ppr $ cmmKind arg) <+>
251 ppr (kindlessCmm arg)
252 _pp_conv = case cconv of
254 _ -> ptext (sLit "foreign") <+> doubleQuotes (ppr cconv)
256 target (CmmLit lit) = pprLit lit
257 target fn' = parens (ppr fn')
259 CmmCall (CmmPrim op) results args safety ret ->
260 pprStmt (CmmCall (CmmCallee (CmmLit lbl) CCallConv)
261 results args safety ret)
263 lbl = CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False)
265 CmmBranch ident -> genBranch ident
266 CmmCondBranch expr ident -> genCondBranch expr ident
267 CmmJump expr params -> genJump expr params
268 CmmReturn params -> genReturn params
269 CmmSwitch arg ids -> genSwitch arg ids
271 -- --------------------------------------------------------------------------
272 -- goto local label. [1], section 6.6
276 genBranch :: BlockId -> SDoc
278 ptext (sLit "goto") <+> pprBlockId ident <> semi
280 -- --------------------------------------------------------------------------
281 -- Conditional. [1], section 6.4
283 -- if (expr) { goto lbl; }
285 genCondBranch :: CmmExpr -> BlockId -> SDoc
286 genCondBranch expr ident =
287 hsep [ ptext (sLit "if")
289 , ptext (sLit "goto")
290 , pprBlockId ident <> semi ]
292 -- --------------------------------------------------------------------------
293 -- A tail call. [1], Section 6.9
295 -- jump foo(a, b, c);
297 genJump :: CmmExpr -> [CmmKinded CmmExpr] -> SDoc
300 hcat [ ptext (sLit "jump")
302 , if isTrivialCmmExpr expr
305 CmmLoad (CmmReg _) _ -> pprExpr expr
306 _ -> parens (pprExpr expr)
308 , parens ( commafy $ map pprKinded args )
311 pprKinded :: Outputable a => (CmmKinded a) -> SDoc
312 pprKinded (CmmKinded a NoHint) = ppr a
313 pprKinded (CmmKinded a PtrHint) = quotes(text "address") <+> ppr a
314 pprKinded (CmmKinded a SignedHint) = quotes(text "signed") <+> ppr a
315 pprKinded (CmmKinded a FloatHint) = quotes(text "float") <+> ppr a
317 -- --------------------------------------------------------------------------
318 -- Return from a function. [1], Section 6.8.2 of version 1.128
322 genReturn :: [CmmKinded CmmExpr] -> SDoc
325 hcat [ ptext (sLit "return")
327 , parens ( commafy $ map ppr args )
330 -- --------------------------------------------------------------------------
331 -- Tabled jump to local label
333 -- The syntax is from [1], section 6.5
335 -- switch [0 .. n] (expr) { case ... ; }
337 genSwitch :: CmmExpr -> [Maybe BlockId] -> SDoc
338 genSwitch expr maybe_ids
340 = let pairs = groupBy snds (zip [0 .. ] maybe_ids )
342 in hang (hcat [ ptext (sLit "switch [0 .. ")
343 , int (length maybe_ids - 1)
345 , if isTrivialCmmExpr expr
347 else parens (pprExpr expr)
350 4 (vcat ( map caseify pairs )) $$ rbrace
353 snds a b = (snd a) == (snd b)
355 caseify :: [(Int,Maybe BlockId)] -> SDoc
356 caseify ixs@((_,Nothing):_)
357 = ptext (sLit "/* impossible: ") <> hcat (intersperse comma (map (int.fst) ixs))
358 <> ptext (sLit " */")
360 = let (is,ids) = unzip as
361 in hsep [ ptext (sLit "case")
362 , hcat (punctuate comma (map int is))
363 , ptext (sLit ": goto")
364 , pprBlockId (head [ id | Just id <- ids]) <> semi ]
366 -- --------------------------------------------------------------------------
370 pprExpr :: CmmExpr -> SDoc
374 pprExpr (CmmMachOp (MO_Add rep)
375 [CmmReg reg, CmmLit (CmmInt (fromIntegral i) rep)])
376 where rep = cmmRegRep reg
377 CmmLit lit -> pprLit lit
380 -- Here's the precedence table from CmmParse.y:
381 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
390 -- We just cope with the common operators for now, the rest will get
391 -- a default conservative behaviour.
393 -- %nonassoc '>=' '>' '<=' '<' '!=' '=='
394 pprExpr1, pprExpr7, pprExpr8 :: CmmExpr -> SDoc
395 pprExpr1 (CmmMachOp op [x,y]) | Just doc <- infixMachOp1 op
396 = pprExpr7 x <+> doc <+> pprExpr7 y
397 pprExpr1 e = pprExpr7 e
399 infixMachOp1, infixMachOp7, infixMachOp8 :: MachOp -> Maybe SDoc
401 infixMachOp1 (MO_Eq _) = Just (ptext (sLit "=="))
402 infixMachOp1 (MO_Ne _) = Just (ptext (sLit "!="))
403 infixMachOp1 (MO_Shl _) = Just (ptext (sLit "<<"))
404 infixMachOp1 (MO_U_Shr _) = Just (ptext (sLit ">>"))
405 infixMachOp1 (MO_U_Ge _) = Just (ptext (sLit ">="))
406 infixMachOp1 (MO_U_Le _) = Just (ptext (sLit "<="))
407 infixMachOp1 (MO_U_Gt _) = Just (char '>')
408 infixMachOp1 (MO_U_Lt _) = Just (char '<')
409 infixMachOp1 _ = Nothing
412 pprExpr7 (CmmMachOp (MO_Add rep1) [x, CmmLit (CmmInt i rep2)]) | i < 0
413 = pprExpr7 (CmmMachOp (MO_Sub rep1) [x, CmmLit (CmmInt (negate i) rep2)])
414 pprExpr7 (CmmMachOp op [x,y]) | Just doc <- infixMachOp7 op
415 = pprExpr7 x <+> doc <+> pprExpr8 y
416 pprExpr7 e = pprExpr8 e
418 infixMachOp7 (MO_Add _) = Just (char '+')
419 infixMachOp7 (MO_Sub _) = Just (char '-')
420 infixMachOp7 _ = Nothing
423 pprExpr8 (CmmMachOp op [x,y]) | Just doc <- infixMachOp8 op
424 = pprExpr8 x <+> doc <+> pprExpr9 y
425 pprExpr8 e = pprExpr9 e
427 infixMachOp8 (MO_U_Quot _) = Just (char '/')
428 infixMachOp8 (MO_Mul _) = Just (char '*')
429 infixMachOp8 (MO_U_Rem _) = Just (char '%')
430 infixMachOp8 _ = Nothing
432 pprExpr9 :: CmmExpr -> SDoc
435 CmmLit lit -> pprLit1 lit
436 CmmLoad expr rep -> ppr rep <> brackets( ppr expr )
437 CmmReg reg -> ppr reg
438 CmmRegOff reg off -> parens (ppr reg <+> char '+' <+> int off)
439 CmmMachOp mop args -> genMachOp mop args
441 genMachOp :: MachOp -> [CmmExpr] -> SDoc
443 | Just doc <- infixMachOp mop = case args of
445 [x,y] -> pprExpr9 x <+> doc <+> pprExpr9 y
448 [x] -> doc <> pprExpr9 x
450 _ -> pprTrace "PprCmm.genMachOp: machop with strange number of args"
452 parens (hcat $ punctuate comma (map pprExpr args)))
455 | isJust (infixMachOp1 mop)
456 || isJust (infixMachOp7 mop)
457 || isJust (infixMachOp8 mop) = parens (pprExpr (CmmMachOp mop args))
459 | otherwise = char '%' <> ppr_op <> parens (commafy (map pprExpr args))
460 where ppr_op = text (map (\c -> if c == ' ' then '_' else c)
462 -- replace spaces in (show mop) with underscores,
465 -- Unsigned ops on the word size of the machine get nice symbols.
466 -- All else get dumped in their ugly format.
468 infixMachOp :: MachOp -> Maybe SDoc
471 MO_And _ -> Just $ char '&'
472 MO_Or _ -> Just $ char '|'
473 MO_Xor _ -> Just $ char '^'
474 MO_Not _ -> Just $ char '~'
475 MO_S_Neg _ -> Just $ char '-' -- there is no unsigned neg :)
478 -- --------------------------------------------------------------------------
480 -- To minimise line noise we adopt the convention that if the literal
481 -- has the natural machine word size, we do not append the type
483 pprLit :: CmmLit -> SDoc
484 pprLit lit = case lit of
486 hcat [ (if i < 0 then parens else id)(integer i)
489 else space <> dcolon <+> ppr rep) ]
491 CmmFloat f rep -> hsep [ rational f, dcolon, ppr rep ]
492 CmmLabel clbl -> pprCLabel clbl
493 CmmLabelOff clbl i -> pprCLabel clbl <> ppr_offset i
494 CmmLabelDiffOff clbl1 clbl2 i -> pprCLabel clbl1 <> char '-'
495 <> pprCLabel clbl2 <> ppr_offset i
497 pprLit1 :: CmmLit -> SDoc
498 pprLit1 lit@(CmmLabelOff {}) = parens (pprLit lit)
499 pprLit1 lit = pprLit lit
501 ppr_offset :: Int -> SDoc
504 | i>=0 = char '+' <> int i
505 | otherwise = char '-' <> int (-i)
507 -- --------------------------------------------------------------------------
509 -- Strings are printed as C strings, and we print them as I8[],
512 pprStatic :: CmmStatic -> SDoc
513 pprStatic s = case s of
514 CmmStaticLit lit -> nest 4 $ ptext (sLit "const") <+> pprLit lit <> semi
515 CmmUninitialised i -> nest 4 $ text "I8" <> brackets (int i)
516 CmmAlign i -> nest 4 $ text "align" <+> int i
517 CmmDataLabel clbl -> pprCLabel clbl <> colon
518 CmmString s' -> nest 4 $ text "I8[]" <+> text (show s')
520 -- --------------------------------------------------------------------------
521 -- Registers, whether local (temps) or global
523 pprReg :: CmmReg -> SDoc
526 CmmLocal local -> pprLocalReg local
527 CmmGlobal global -> pprGlobalReg global
528 CmmStack slot -> ppr slot
531 -- We only print the type of the local reg if it isn't wordRep
533 pprLocalReg :: LocalReg -> SDoc
534 pprLocalReg (LocalReg uniq rep follow)
535 = hcat [ char '_', ppr uniq, ty ] where
536 ty = if rep == wordRep && follow == GCKindNonPtr
538 else dcolon <> ptr <> ppr rep
539 ptr = if follow == GCKindNonPtr
541 else doubleQuotes (text "ptr")
543 -- needs to be kept in syn with Cmm.hs.GlobalReg
545 pprGlobalReg :: GlobalReg -> SDoc
548 VanillaReg n -> char 'R' <> int n
549 FloatReg n -> char 'F' <> int n
550 DoubleReg n -> char 'D' <> int n
551 LongReg n -> char 'L' <> int n
552 Sp -> ptext (sLit "Sp")
553 SpLim -> ptext (sLit "SpLim")
554 Hp -> ptext (sLit "Hp")
555 HpLim -> ptext (sLit "HpLim")
556 CurrentTSO -> ptext (sLit "CurrentTSO")
557 CurrentNursery -> ptext (sLit "CurrentNursery")
558 HpAlloc -> ptext (sLit "HpAlloc")
559 GCEnter1 -> ptext (sLit "stg_gc_enter_1")
560 GCFun -> ptext (sLit "stg_gc_fun")
561 BaseReg -> ptext (sLit "BaseReg")
562 PicBaseReg -> ptext (sLit "PicBaseReg")
564 -- --------------------------------------------------------------------------
567 pprSection :: Section -> SDoc
568 pprSection s = case s of
569 Text -> section <+> doubleQuotes (ptext (sLit "text"))
570 Data -> section <+> doubleQuotes (ptext (sLit "data"))
571 ReadOnlyData -> section <+> doubleQuotes (ptext (sLit "readonly"))
572 ReadOnlyData16 -> section <+> doubleQuotes (ptext (sLit "readonly16"))
573 RelocatableReadOnlyData
574 -> section <+> doubleQuotes (ptext (sLit "relreadonly"))
575 UninitialisedData -> section <+> doubleQuotes (ptext (sLit "uninitialised"))
576 OtherSection s' -> section <+> doubleQuotes (text s')
578 section = ptext (sLit "section")
580 -- --------------------------------------------------------------------------
583 pprBlockId :: BlockId -> SDoc
584 pprBlockId b = ppr $ getUnique b
586 -----------------------------------------------------------------------------
588 commafy :: [SDoc] -> SDoc
589 commafy xs = fsep $ punctuate comma xs