e9941b81ff91821a35f988eb921f5612dc2d00a6
[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 pprSize,
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 Size
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 size for an instruction suffix.
212 pprSize :: Size -> SDoc
213 pprSize 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.pprSize: no match")
223
224
225 -- | Pretty print a size for an instruction suffix.
226 -- eg LD is 32bit on sparc, but LDD is 64 bit.
227 pprStSize :: Size -> SDoc
228 pprStSize 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.pprSize: 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 (cmmTypeSize $ 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 size addr reg)
382 = hcat [
383 ptext (sLit "\tld"),
384 pprSize size,
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 pprSize for ST..
400 pprInstr (ST size reg addr)
401 = hcat [
402 ptext (sLit "\tst"),
403 pprStSize size,
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 size reg1 reg2)
479 = pprSizeRegReg (sLit "fabs") size reg1 reg2
480
481 pprInstr (FADD size reg1 reg2 reg3)
482 = pprSizeRegRegReg (sLit "fadd") size reg1 reg2 reg3
483
484 pprInstr (FCMP e size reg1 reg2)
485 = pprSizeRegReg (if e then sLit "fcmpe" else sLit "fcmp") size reg1 reg2
486
487 pprInstr (FDIV size reg1 reg2 reg3)
488 = pprSizeRegRegReg (sLit "fdiv") size reg1 reg2 reg3
489
490 pprInstr (FMOV size reg1 reg2)
491 = pprSizeRegReg (sLit "fmov") size reg1 reg2
492
493 pprInstr (FMUL size reg1 reg2 reg3)
494 = pprSizeRegRegReg (sLit "fmul") size reg1 reg2 reg3
495
496 pprInstr (FNEG size reg1 reg2)
497 = pprSizeRegReg (sLit "fneg") size reg1 reg2
498
499 pprInstr (FSQRT size reg1 reg2)
500 = pprSizeRegReg (sLit "fsqrt") size reg1 reg2
501
502 pprInstr (FSUB size reg1 reg2 reg3)
503 = pprSizeRegRegReg (sLit "fsub") size reg1 reg2 reg3
504
505 pprInstr (FxTOy size1 size2 reg1 reg2)
506 = hcat [
507 ptext (sLit "\tf"),
508 ptext
509 (case size1 of
510 II32 -> sLit "ito"
511 FF32 -> sLit "sto"
512 FF64 -> sLit "dto"
513 _ -> panic "SPARC.Ppr.pprInstr.FxToY: no match"),
514 ptext
515 (case size2 of
516 II32 -> sLit "i\t"
517 II64 -> sLit "x\t"
518 FF32 -> sLit "s\t"
519 FF64 -> sLit "d\t"
520 _ -> panic "SPARC.Ppr.pprInstr.FxToY: no match"),
521 pprReg reg1, comma, pprReg reg2
522 ]
523
524
525 pprInstr (BI cond b blockid)
526 = hcat [
527 ptext (sLit "\tb"), pprCond cond,
528 if b then pp_comma_a else empty,
529 char '\t',
530 ppr (mkAsmTempLabel (getUnique blockid))
531 ]
532
533 pprInstr (BF cond b blockid)
534 = hcat [
535 ptext (sLit "\tfb"), pprCond cond,
536 if b then pp_comma_a else empty,
537 char '\t',
538 ppr (mkAsmTempLabel (getUnique blockid))
539 ]
540
541 pprInstr (JMP addr) = ptext (sLit "\tjmp\t") <> pprAddr addr
542 pprInstr (JMP_TBL op _ _) = pprInstr (JMP op)
543
544 pprInstr (CALL (Left imm) n _)
545 = hcat [ ptext (sLit "\tcall\t"), pprImm imm, comma, int n ]
546
547 pprInstr (CALL (Right reg) n _)
548 = hcat [ ptext (sLit "\tcall\t"), pprReg reg, comma, int n ]
549
550
551 -- | Pretty print a RI
552 pprRI :: RI -> SDoc
553 pprRI (RIReg r) = pprReg r
554 pprRI (RIImm r) = pprImm r
555
556
557 -- | Pretty print a two reg instruction.
558 pprSizeRegReg :: LitString -> Size -> Reg -> Reg -> SDoc
559 pprSizeRegReg name size reg1 reg2
560 = hcat [
561 char '\t',
562 ptext name,
563 (case size of
564 FF32 -> ptext (sLit "s\t")
565 FF64 -> ptext (sLit "d\t")
566 _ -> panic "SPARC.Ppr.pprSizeRegReg: no match"),
567
568 pprReg reg1,
569 comma,
570 pprReg reg2
571 ]
572
573
574 -- | Pretty print a three reg instruction.
575 pprSizeRegRegReg :: LitString -> Size -> Reg -> Reg -> Reg -> SDoc
576 pprSizeRegRegReg name size reg1 reg2 reg3
577 = hcat [
578 char '\t',
579 ptext name,
580 (case size of
581 FF32 -> ptext (sLit "s\t")
582 FF64 -> ptext (sLit "d\t")
583 _ -> panic "SPARC.Ppr.pprSizeRegReg: no match"),
584 pprReg reg1,
585 comma,
586 pprReg reg2,
587 comma,
588 pprReg reg3
589 ]
590
591
592 -- | Pretty print an instruction of two regs and a ri.
593 pprRegRIReg :: LitString -> Bool -> Reg -> RI -> Reg -> SDoc
594 pprRegRIReg name b reg1 ri reg2
595 = hcat [
596 char '\t',
597 ptext name,
598 if b then ptext (sLit "cc\t") else char '\t',
599 pprReg reg1,
600 comma,
601 pprRI ri,
602 comma,
603 pprReg reg2
604 ]
605
606 {-
607 pprRIReg :: LitString -> Bool -> RI -> Reg -> SDoc
608 pprRIReg name b ri reg1
609 = hcat [
610 char '\t',
611 ptext name,
612 if b then ptext (sLit "cc\t") else char '\t',
613 pprRI ri,
614 comma,
615 pprReg reg1
616 ]
617 -}
618
619 {-
620 pp_ld_lbracket :: SDoc
621 pp_ld_lbracket = ptext (sLit "\tld\t[")
622 -}
623
624 pp_rbracket_comma :: SDoc
625 pp_rbracket_comma = text "],"
626
627
628 pp_comma_lbracket :: SDoc
629 pp_comma_lbracket = text ",["
630
631
632 pp_comma_a :: SDoc
633 pp_comma_a = text ",a"
634