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