Fix todo in compiler/nativeGen: Rename Size to Format
[ghc.git] / compiler / nativeGen / SPARC / Ppr.hs
1 {-# LANGUAGE CPP #-}
2
3 -----------------------------------------------------------------------------
4 --
5 -- Pretty-printing assembly language
6 --
7 -- (c) The University of Glasgow 1993-2005
8 --
9 -----------------------------------------------------------------------------
10
11 {-# OPTIONS_GHC -fno-warn-orphans #-}
12
13 module SPARC.Ppr (
14 pprNatCmmDecl,
15 pprBasicBlock,
16 pprSectionHeader,
17 pprData,
18 pprInstr,
19 pprFormat,
20 pprImm,
21 pprDataItem
22 )
23
24 where
25
26 #include "HsVersions.h"
27 #include "nativeGen/NCG.h"
28
29 import SPARC.Regs
30 import SPARC.Instr
31 import SPARC.Cond
32 import SPARC.Imm
33 import SPARC.AddrMode
34 import SPARC.Base
35 import Instruction
36 import Reg
37 import Format
38 import PprBase
39
40 import Cmm hiding (topInfoTable)
41 import PprCmm()
42 import CLabel
43 import BlockId
44
45 import Unique ( Uniquable(..), pprUnique )
46 import Outputable
47 import Platform
48 import FastString
49 import Data.Word
50
51 -- -----------------------------------------------------------------------------
52 -- Printing this stuff out
53
54 pprNatCmmDecl :: NatCmmDecl CmmStatics Instr -> SDoc
55 pprNatCmmDecl (CmmData section dats) =
56 pprSectionHeader section $$ pprDatas dats
57
58 pprNatCmmDecl proc@(CmmProc top_info lbl _ (ListGraph blocks)) =
59 case topInfoTable proc of
60 Nothing ->
61 case blocks of
62 [] -> -- special case for split markers:
63 pprLabel lbl
64 blocks -> -- special case for code without info table:
65 pprSectionHeader Text $$
66 pprLabel lbl $$ -- blocks guaranteed not null, so label needed
67 vcat (map (pprBasicBlock top_info) blocks)
68
69 Just (Statics info_lbl _) ->
70 sdocWithPlatform $ \platform ->
71 (if platformHasSubsectionsViaSymbols platform
72 then pprSectionHeader Text $$
73 ppr (mkDeadStripPreventer info_lbl) <> char ':'
74 else empty) $$
75 vcat (map (pprBasicBlock top_info) blocks) $$
76 -- above: Even the first block gets a label, because with branch-chain
77 -- elimination, it might be the target of a goto.
78 (if platformHasSubsectionsViaSymbols platform
79 then
80 -- See Note [Subsections Via Symbols]
81 text "\t.long "
82 <+> ppr info_lbl
83 <+> char '-'
84 <+> ppr (mkDeadStripPreventer info_lbl)
85 else empty)
86
87
88 pprBasicBlock :: BlockEnv CmmStatics -> NatBasicBlock Instr -> SDoc
89 pprBasicBlock info_env (BasicBlock blockid instrs)
90 = maybe_infotable $$
91 pprLabel (mkAsmTempLabel (getUnique blockid)) $$
92 vcat (map pprInstr instrs)
93 where
94 maybe_infotable = case mapLookup blockid info_env of
95 Nothing -> empty
96 Just (Statics info_lbl info) ->
97 pprSectionHeader Text $$
98 vcat (map pprData info) $$
99 pprLabel info_lbl
100
101
102 pprDatas :: CmmStatics -> SDoc
103 pprDatas (Statics lbl dats) = vcat (pprLabel lbl : map pprData dats)
104
105 pprData :: CmmStatic -> SDoc
106 pprData (CmmString str) = pprASCII str
107 pprData (CmmUninitialised bytes) = ptext (sLit ".skip ") <> int bytes
108 pprData (CmmStaticLit lit) = pprDataItem lit
109
110 pprGloblDecl :: CLabel -> SDoc
111 pprGloblDecl lbl
112 | not (externallyVisibleCLabel lbl) = empty
113 | otherwise = ptext (sLit ".global ") <> ppr lbl
114
115 pprTypeAndSizeDecl :: CLabel -> SDoc
116 pprTypeAndSizeDecl lbl
117 = sdocWithPlatform $ \platform ->
118 if platformOS platform == OSLinux && externallyVisibleCLabel lbl
119 then ptext (sLit ".type ") <> ppr lbl <> ptext (sLit ", @object")
120 else empty
121
122 pprLabel :: CLabel -> SDoc
123 pprLabel lbl = pprGloblDecl lbl
124 $$ pprTypeAndSizeDecl lbl
125 $$ (ppr lbl <> char ':')
126
127
128 pprASCII :: [Word8] -> SDoc
129 pprASCII str
130 = vcat (map do1 str) $$ do1 0
131 where
132 do1 :: Word8 -> SDoc
133 do1 w = ptext (sLit "\t.byte\t") <> int (fromIntegral w)
134
135
136 -- -----------------------------------------------------------------------------
137 -- pprInstr: print an 'Instr'
138
139 instance Outputable Instr where
140 ppr instr = pprInstr instr
141
142
143 -- | Pretty print a register.
144 pprReg :: Reg -> SDoc
145 pprReg reg
146 = case reg of
147 RegVirtual vr
148 -> case vr of
149 VirtualRegI u -> text "%vI_" <> pprUnique u
150 VirtualRegHi u -> text "%vHi_" <> pprUnique u
151 VirtualRegF u -> text "%vF_" <> pprUnique u
152 VirtualRegD u -> text "%vD_" <> pprUnique u
153 VirtualRegSSE u -> text "%vSSE_" <> pprUnique u
154
155 RegReal rr
156 -> case rr of
157 RealRegSingle r1
158 -> pprReg_ofRegNo r1
159
160 RealRegPair r1 r2
161 -> text "(" <> pprReg_ofRegNo r1
162 <> text "|" <> pprReg_ofRegNo r2
163 <> text ")"
164
165
166
167 -- | Pretty print a register name, based on this register number.
168 -- The definition has been unfolded so we get a jump-table in the
169 -- object code. This function is called quite a lot when emitting
170 -- the asm file..
171 --
172 pprReg_ofRegNo :: Int -> SDoc
173 pprReg_ofRegNo i
174 = ptext
175 (case i of {
176 0 -> sLit "%g0"; 1 -> sLit "%g1";
177 2 -> sLit "%g2"; 3 -> sLit "%g3";
178 4 -> sLit "%g4"; 5 -> sLit "%g5";
179 6 -> sLit "%g6"; 7 -> sLit "%g7";
180 8 -> sLit "%o0"; 9 -> sLit "%o1";
181 10 -> sLit "%o2"; 11 -> sLit "%o3";
182 12 -> sLit "%o4"; 13 -> sLit "%o5";
183 14 -> sLit "%o6"; 15 -> sLit "%o7";
184 16 -> sLit "%l0"; 17 -> sLit "%l1";
185 18 -> sLit "%l2"; 19 -> sLit "%l3";
186 20 -> sLit "%l4"; 21 -> sLit "%l5";
187 22 -> sLit "%l6"; 23 -> sLit "%l7";
188 24 -> sLit "%i0"; 25 -> sLit "%i1";
189 26 -> sLit "%i2"; 27 -> sLit "%i3";
190 28 -> sLit "%i4"; 29 -> sLit "%i5";
191 30 -> sLit "%i6"; 31 -> sLit "%i7";
192 32 -> sLit "%f0"; 33 -> sLit "%f1";
193 34 -> sLit "%f2"; 35 -> sLit "%f3";
194 36 -> sLit "%f4"; 37 -> sLit "%f5";
195 38 -> sLit "%f6"; 39 -> sLit "%f7";
196 40 -> sLit "%f8"; 41 -> sLit "%f9";
197 42 -> sLit "%f10"; 43 -> sLit "%f11";
198 44 -> sLit "%f12"; 45 -> sLit "%f13";
199 46 -> sLit "%f14"; 47 -> sLit "%f15";
200 48 -> sLit "%f16"; 49 -> sLit "%f17";
201 50 -> sLit "%f18"; 51 -> sLit "%f19";
202 52 -> sLit "%f20"; 53 -> sLit "%f21";
203 54 -> sLit "%f22"; 55 -> sLit "%f23";
204 56 -> sLit "%f24"; 57 -> sLit "%f25";
205 58 -> sLit "%f26"; 59 -> sLit "%f27";
206 60 -> sLit "%f28"; 61 -> sLit "%f29";
207 62 -> sLit "%f30"; 63 -> sLit "%f31";
208 _ -> sLit "very naughty sparc register" })
209
210
211 -- | Pretty print a format for an instruction suffix.
212 pprFormat :: Format -> SDoc
213 pprFormat x
214 = ptext
215 (case x of
216 II8 -> sLit "ub"
217 II16 -> sLit "uh"
218 II32 -> sLit ""
219 II64 -> sLit "d"
220 FF32 -> sLit ""
221 FF64 -> sLit "d"
222 _ -> panic "SPARC.Ppr.pprFormat: no match")
223
224
225 -- | Pretty print a format for an instruction suffix.
226 -- eg LD is 32bit on sparc, but LDD is 64 bit.
227 pprStFormat :: Format -> SDoc
228 pprStFormat x
229 = ptext
230 (case x of
231 II8 -> sLit "b"
232 II16 -> sLit "h"
233 II32 -> sLit ""
234 II64 -> sLit "x"
235 FF32 -> sLit ""
236 FF64 -> sLit "d"
237 _ -> panic "SPARC.Ppr.pprFormat: no match")
238
239
240 -- | Pretty print a condition code.
241 pprCond :: Cond -> SDoc
242 pprCond c
243 = ptext
244 (case c of
245 ALWAYS -> sLit ""
246 NEVER -> sLit "n"
247 GEU -> sLit "geu"
248 LU -> sLit "lu"
249 EQQ -> sLit "e"
250 GTT -> sLit "g"
251 GE -> sLit "ge"
252 GU -> sLit "gu"
253 LTT -> sLit "l"
254 LE -> sLit "le"
255 LEU -> sLit "leu"
256 NE -> sLit "ne"
257 NEG -> sLit "neg"
258 POS -> sLit "pos"
259 VC -> sLit "vc"
260 VS -> sLit "vs")
261
262
263 -- | Pretty print an address mode.
264 pprAddr :: AddrMode -> SDoc
265 pprAddr am
266 = case am of
267 AddrRegReg r1 (RegReal (RealRegSingle 0))
268 -> pprReg r1
269
270 AddrRegReg r1 r2
271 -> hcat [ pprReg r1, char '+', pprReg r2 ]
272
273 AddrRegImm r1 (ImmInt i)
274 | i == 0 -> pprReg r1
275 | not (fits13Bits i) -> largeOffsetError i
276 | otherwise -> hcat [ pprReg r1, pp_sign, int i ]
277 where
278 pp_sign = if i > 0 then char '+' else empty
279
280 AddrRegImm r1 (ImmInteger i)
281 | i == 0 -> pprReg r1
282 | not (fits13Bits i) -> largeOffsetError i
283 | otherwise -> hcat [ pprReg r1, pp_sign, integer i ]
284 where
285 pp_sign = if i > 0 then char '+' else empty
286
287 AddrRegImm r1 imm
288 -> hcat [ pprReg r1, char '+', pprImm imm ]
289
290
291 -- | Pretty print an immediate value.
292 pprImm :: Imm -> SDoc
293 pprImm imm
294 = case imm of
295 ImmInt i -> int i
296 ImmInteger i -> integer i
297 ImmCLbl l -> ppr l
298 ImmIndex l i -> ppr l <> char '+' <> int i
299 ImmLit s -> s
300
301 ImmConstantSum a b
302 -> pprImm a <> char '+' <> pprImm b
303
304 ImmConstantDiff a b
305 -> pprImm a <> char '-' <> lparen <> pprImm b <> rparen
306
307 LO i
308 -> hcat [ text "%lo(", pprImm i, rparen ]
309
310 HI i
311 -> hcat [ text "%hi(", pprImm i, rparen ]
312
313 -- these should have been converted to bytes and placed
314 -- in the data section.
315 ImmFloat _ -> ptext (sLit "naughty float immediate")
316 ImmDouble _ -> ptext (sLit "naughty double immediate")
317
318
319 -- | Pretty print a section \/ segment header.
320 -- On SPARC all the data sections must be at least 8 byte aligned
321 -- incase we store doubles in them.
322 --
323 pprSectionHeader :: Section -> SDoc
324 pprSectionHeader seg = case seg of
325 Text -> text ".text\n\t.align 4"
326 Data -> text ".data\n\t.align 8"
327 ReadOnlyData -> text ".text\n\t.align 8"
328 RelocatableReadOnlyData
329 -> text ".text\n\t.align 8"
330 UninitialisedData -> text ".bss\n\t.align 8"
331 ReadOnlyData16 -> text ".data\n\t.align 16"
332 OtherSection _ -> panic "PprMach.pprSectionHeader: unknown section"
333
334
335 -- | Pretty print a data item.
336 pprDataItem :: CmmLit -> SDoc
337 pprDataItem lit
338 = sdocWithDynFlags $ \dflags ->
339 vcat (ppr_item (cmmTypeFormat $ cmmLitType dflags lit) lit)
340 where
341 imm = litToImm lit
342
343 ppr_item II8 _ = [ptext (sLit "\t.byte\t") <> pprImm imm]
344 ppr_item II32 _ = [ptext (sLit "\t.long\t") <> pprImm imm]
345
346 ppr_item FF32 (CmmFloat r _)
347 = let bs = floatToBytes (fromRational r)
348 in map (\b -> ptext (sLit "\t.byte\t") <> pprImm (ImmInt b)) bs
349
350 ppr_item FF64 (CmmFloat r _)
351 = let bs = doubleToBytes (fromRational r)
352 in map (\b -> ptext (sLit "\t.byte\t") <> pprImm (ImmInt b)) bs
353
354 ppr_item II16 _ = [ptext (sLit "\t.short\t") <> pprImm imm]
355 ppr_item II64 _ = [ptext (sLit "\t.quad\t") <> pprImm imm]
356 ppr_item _ _ = panic "SPARC.Ppr.pprDataItem: no match"
357
358
359 -- | Pretty print an instruction.
360 pprInstr :: Instr -> SDoc
361
362 -- nuke comments.
363 pprInstr (COMMENT _)
364 = empty
365
366 pprInstr (DELTA d)
367 = pprInstr (COMMENT (mkFastString ("\tdelta = " ++ show d)))
368
369 -- Newblocks and LData should have been slurped out before producing the .s file.
370 pprInstr (NEWBLOCK _)
371 = panic "X86.Ppr.pprInstr: NEWBLOCK"
372
373 pprInstr (LDATA _ _)
374 = panic "PprMach.pprInstr: LDATA"
375
376 -- 64 bit FP loads are expanded into individual instructions in CodeGen.Expand
377 pprInstr (LD FF64 _ reg)
378 | RegReal (RealRegSingle{}) <- reg
379 = panic "SPARC.Ppr: not emitting potentially misaligned LD FF64 instr"
380
381 pprInstr (LD format addr reg)
382 = hcat [
383 ptext (sLit "\tld"),
384 pprFormat format,
385 char '\t',
386 lbrack,
387 pprAddr addr,
388 pp_rbracket_comma,
389 pprReg reg
390 ]
391
392 -- 64 bit FP storees are expanded into individual instructions in CodeGen.Expand
393 pprInstr (ST FF64 reg _)
394 | RegReal (RealRegSingle{}) <- reg
395 = panic "SPARC.Ppr: not emitting potentially misaligned ST FF64 instr"
396
397 -- no distinction is made between signed and unsigned bytes on stores for the
398 -- Sparc opcodes (at least I cannot see any, and gas is nagging me --SOF),
399 -- so we call a special-purpose pprFormat for ST..
400 pprInstr (ST format reg addr)
401 = hcat [
402 ptext (sLit "\tst"),
403 pprStFormat format,
404 char '\t',
405 pprReg reg,
406 pp_comma_lbracket,
407 pprAddr addr,
408 rbrack
409 ]
410
411
412 pprInstr (ADD x cc reg1 ri reg2)
413 | not x && not cc && riZero ri
414 = hcat [ ptext (sLit "\tmov\t"), pprReg reg1, comma, pprReg reg2 ]
415
416 | otherwise
417 = pprRegRIReg (if x then sLit "addx" else sLit "add") cc reg1 ri reg2
418
419
420 pprInstr (SUB x cc reg1 ri reg2)
421 | not x && cc && reg2 == g0
422 = hcat [ ptext (sLit "\tcmp\t"), pprReg reg1, comma, pprRI ri ]
423
424 | not x && not cc && riZero ri
425 = hcat [ ptext (sLit "\tmov\t"), pprReg reg1, comma, pprReg reg2 ]
426
427 | otherwise
428 = pprRegRIReg (if x then sLit "subx" else sLit "sub") cc reg1 ri reg2
429
430 pprInstr (AND b reg1 ri reg2) = pprRegRIReg (sLit "and") b reg1 ri reg2
431
432 pprInstr (ANDN b reg1 ri reg2) = pprRegRIReg (sLit "andn") b reg1 ri reg2
433
434 pprInstr (OR b reg1 ri reg2)
435 | not b && reg1 == g0
436 = let doit = hcat [ ptext (sLit "\tmov\t"), pprRI ri, comma, pprReg reg2 ]
437 in case ri of
438 RIReg rrr | rrr == reg2 -> empty
439 _ -> doit
440
441 | otherwise
442 = pprRegRIReg (sLit "or") b reg1 ri reg2
443
444 pprInstr (ORN b reg1 ri reg2) = pprRegRIReg (sLit "orn") b reg1 ri reg2
445
446 pprInstr (XOR b reg1 ri reg2) = pprRegRIReg (sLit "xor") b reg1 ri reg2
447 pprInstr (XNOR b reg1 ri reg2) = pprRegRIReg (sLit "xnor") b reg1 ri reg2
448
449 pprInstr (SLL reg1 ri reg2) = pprRegRIReg (sLit "sll") False reg1 ri reg2
450 pprInstr (SRL reg1 ri reg2) = pprRegRIReg (sLit "srl") False reg1 ri reg2
451 pprInstr (SRA reg1 ri reg2) = pprRegRIReg (sLit "sra") False reg1 ri reg2
452
453 pprInstr (RDY rd) = ptext (sLit "\trd\t%y,") <> pprReg rd
454 pprInstr (WRY reg1 reg2)
455 = ptext (sLit "\twr\t")
456 <> pprReg reg1
457 <> char ','
458 <> pprReg reg2
459 <> char ','
460 <> ptext (sLit "%y")
461
462 pprInstr (SMUL b reg1 ri reg2) = pprRegRIReg (sLit "smul") b reg1 ri reg2
463 pprInstr (UMUL b reg1 ri reg2) = pprRegRIReg (sLit "umul") b reg1 ri reg2
464 pprInstr (SDIV b reg1 ri reg2) = pprRegRIReg (sLit "sdiv") b reg1 ri reg2
465 pprInstr (UDIV b reg1 ri reg2) = pprRegRIReg (sLit "udiv") b reg1 ri reg2
466
467 pprInstr (SETHI imm reg)
468 = hcat [
469 ptext (sLit "\tsethi\t"),
470 pprImm imm,
471 comma,
472 pprReg reg
473 ]
474
475 pprInstr NOP
476 = ptext (sLit "\tnop")
477
478 pprInstr (FABS format reg1 reg2)
479 = pprFormatRegReg (sLit "fabs") format reg1 reg2
480
481 pprInstr (FADD format reg1 reg2 reg3)
482 = pprFormatRegRegReg (sLit "fadd") format reg1 reg2 reg3
483
484 pprInstr (FCMP e format reg1 reg2)
485 = pprFormatRegReg (if e then sLit "fcmpe" else sLit "fcmp")
486 format reg1 reg2
487
488 pprInstr (FDIV format reg1 reg2 reg3)
489 = pprFormatRegRegReg (sLit "fdiv") format reg1 reg2 reg3
490
491 pprInstr (FMOV format reg1 reg2)
492 = pprFormatRegReg (sLit "fmov") format reg1 reg2
493
494 pprInstr (FMUL format reg1 reg2 reg3)
495 = pprFormatRegRegReg (sLit "fmul") format reg1 reg2 reg3
496
497 pprInstr (FNEG format reg1 reg2)
498 = pprFormatRegReg (sLit "fneg") format reg1 reg2
499
500 pprInstr (FSQRT format reg1 reg2)
501 = pprFormatRegReg (sLit "fsqrt") format reg1 reg2
502
503 pprInstr (FSUB format reg1 reg2 reg3)
504 = pprFormatRegRegReg (sLit "fsub") format reg1 reg2 reg3
505
506 pprInstr (FxTOy format1 format2 reg1 reg2)
507 = hcat [
508 ptext (sLit "\tf"),
509 ptext
510 (case format1 of
511 II32 -> sLit "ito"
512 FF32 -> sLit "sto"
513 FF64 -> sLit "dto"
514 _ -> panic "SPARC.Ppr.pprInstr.FxToY: no match"),
515 ptext
516 (case format2 of
517 II32 -> sLit "i\t"
518 II64 -> sLit "x\t"
519 FF32 -> sLit "s\t"
520 FF64 -> sLit "d\t"
521 _ -> panic "SPARC.Ppr.pprInstr.FxToY: no match"),
522 pprReg reg1, comma, pprReg reg2
523 ]
524
525
526 pprInstr (BI cond b blockid)
527 = hcat [
528 ptext (sLit "\tb"), pprCond cond,
529 if b then pp_comma_a else empty,
530 char '\t',
531 ppr (mkAsmTempLabel (getUnique blockid))
532 ]
533
534 pprInstr (BF cond b blockid)
535 = hcat [
536 ptext (sLit "\tfb"), pprCond cond,
537 if b then pp_comma_a else empty,
538 char '\t',
539 ppr (mkAsmTempLabel (getUnique blockid))
540 ]
541
542 pprInstr (JMP addr) = ptext (sLit "\tjmp\t") <> pprAddr addr
543 pprInstr (JMP_TBL op _ _) = pprInstr (JMP op)
544
545 pprInstr (CALL (Left imm) n _)
546 = hcat [ ptext (sLit "\tcall\t"), pprImm imm, comma, int n ]
547
548 pprInstr (CALL (Right reg) n _)
549 = hcat [ ptext (sLit "\tcall\t"), pprReg reg, comma, int n ]
550
551
552 -- | Pretty print a RI
553 pprRI :: RI -> SDoc
554 pprRI (RIReg r) = pprReg r
555 pprRI (RIImm r) = pprImm r
556
557
558 -- | Pretty print a two reg instruction.
559 pprFormatRegReg :: LitString -> Format -> Reg -> Reg -> SDoc
560 pprFormatRegReg name format reg1 reg2
561 = hcat [
562 char '\t',
563 ptext name,
564 (case format of
565 FF32 -> ptext (sLit "s\t")
566 FF64 -> ptext (sLit "d\t")
567 _ -> panic "SPARC.Ppr.pprFormatRegReg: no match"),
568
569 pprReg reg1,
570 comma,
571 pprReg reg2
572 ]
573
574
575 -- | Pretty print a three reg instruction.
576 pprFormatRegRegReg :: LitString -> Format -> Reg -> Reg -> Reg -> SDoc
577 pprFormatRegRegReg name format reg1 reg2 reg3
578 = hcat [
579 char '\t',
580 ptext name,
581 (case format of
582 FF32 -> ptext (sLit "s\t")
583 FF64 -> ptext (sLit "d\t")
584 _ -> panic "SPARC.Ppr.pprFormatRegReg: no match"),
585 pprReg reg1,
586 comma,
587 pprReg reg2,
588 comma,
589 pprReg reg3
590 ]
591
592
593 -- | Pretty print an instruction of two regs and a ri.
594 pprRegRIReg :: LitString -> Bool -> Reg -> RI -> Reg -> SDoc
595 pprRegRIReg name b reg1 ri reg2
596 = hcat [
597 char '\t',
598 ptext name,
599 if b then ptext (sLit "cc\t") else char '\t',
600 pprReg reg1,
601 comma,
602 pprRI ri,
603 comma,
604 pprReg reg2
605 ]
606
607 {-
608 pprRIReg :: LitString -> Bool -> RI -> Reg -> SDoc
609 pprRIReg name b ri reg1
610 = hcat [
611 char '\t',
612 ptext name,
613 if b then ptext (sLit "cc\t") else char '\t',
614 pprRI ri,
615 comma,
616 pprReg reg1
617 ]
618 -}
619
620 {-
621 pp_ld_lbracket :: SDoc
622 pp_ld_lbracket = ptext (sLit "\tld\t[")
623 -}
624
625 pp_rbracket_comma :: SDoc
626 pp_rbracket_comma = text "],"
627
628
629 pp_comma_lbracket :: SDoc
630 pp_comma_lbracket = text ",["
631
632
633 pp_comma_a :: SDoc
634 pp_comma_a = text ",a"
635