2 % (c) The AQUA Project, Glasgow University, 1993-1998
4 \section[MachMisc]{Description of various machine-specific things}
7 #include "nativeGen/NCG.h"
15 volatileSaves, volatileRestores,
17 targetMaxDouble, targetMaxInt, targetMinDouble, targetMinInt,
23 Instr(..), IF_ARCH_i386(Operand(..) COMMA,)
26 IF_ARCH_i386(i386_insert_ffrees COMMA,)
34 RI(..), riZero, fpRelEA, moveSp, fPair
38 #include "HsVersions.h"
39 #include "../includes/config.h"
41 import AbsCSyn ( MagicId(..) )
42 import AbsCUtils ( magicIdPrimRep )
43 import CLabel ( CLabel, isAsmTemp )
44 import Literal ( mkMachInt, Literal(..) )
45 import MachRegs ( callerSaves,
46 get_MagicId_addr, get_MagicId_reg_or_addr,
47 Imm(..), Reg(..), MachRegsAddr(..)
48 # if sparc_TARGET_ARCH
52 import PrimRep ( PrimRep(..) )
53 import Stix ( StixStmt(..), StixExpr(..), StixReg(..),
54 CodeSegment, DestInfo(..) )
55 import Panic ( panic )
57 import Outputable ( pprPanic, ppr, showSDoc )
58 import IOExts ( trace )
59 import Config ( cLeadingUnderscore )
62 import Maybe ( catMaybes )
66 underscorePrefix :: Bool -- leading underscore on assembler labels?
67 underscorePrefix = (cLeadingUnderscore == "YES")
69 ---------------------------
70 fmtAsmLbl :: String -> String -- for formatting labels
74 {- The alpha assembler likes temporary labels to look like $L123
75 instead of L123. (Don't toss the L, because then Lf28
84 % ----------------------------------------------------------------
86 We (allegedly) put the first six C-call arguments in registers;
87 where do we start putting the rest of them?
89 eXTRA_STK_ARGS_HERE :: Int
91 = IF_ARCH_alpha(0, IF_ARCH_i386(23{-6x4bytes-}, IF_ARCH_sparc(23,???)))
94 % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
96 Now the volatile saves and restores. We add the basic guys to the
97 list of ``user'' registers provided. Note that there are more basic
98 registers on the restore list, because some are reloaded from
101 (@volatileRestores@ used only for wrapper-hungry PrimOps.)
104 volatileSaves, volatileRestores :: [MagicId] -> [StixStmt]
106 volatileSaves = volatileSavesOrRestores True
107 volatileRestores = volatileSavesOrRestores False
109 save_cands = [BaseReg,Sp,Su,SpLim,Hp,HpLim]
110 restore_cands = save_cands
112 volatileSavesOrRestores do_saves vols
113 = catMaybes (map mkCode vols)
116 | not (callerSaves mid)
118 | otherwise -- must be callee-saves ...
119 = case get_MagicId_reg_or_addr mid of
120 -- If stored in BaseReg, we ain't interested
124 -- OK, it's callee-saves, and in a real reg (rrno).
125 -- We have to cook up some transfer code.
126 {- Note that the use of (StixMagicId mid) here is a bit subtle.
127 Here, we only create those for MagicIds which are stored in
128 a real reg on this arch -- the preceding case on the result
129 of get_MagicId_reg_or_addr guarantees this. Later, when
130 selecting insns, that means these assignments are sure to turn
131 into real reg-to-mem or mem-to-reg moves, rather than being
132 pointless moves from some address in the reg-table
135 -> Just (StAssignMem rep addr
136 (StReg (StixMagicId mid)))
138 -> Just (StAssignReg rep (StixMagicId mid)
141 rep = magicIdPrimRep mid
142 addr = get_MagicId_addr mid
145 % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147 Obviously slightly weedy
148 (Note that the floating point values aren't terribly important.)
151 targetMinDouble = MachDouble (-1.7976931348623157e+308)
152 targetMaxDouble = MachDouble (1.7976931348623157e+308)
153 targetMinInt = mkMachInt (-2147483648)
154 targetMaxInt = mkMachInt 2147483647
157 % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159 This algorithm for determining the $\log_2$ of exact powers of 2 comes
160 from GCC. It requires bit manipulation primitives, and we use GHC
167 exactLog2 :: Integer -> Maybe Integer
169 = if (x <= 0 || x >= 2147483648) then
172 case iUnbox (fromInteger x) of { x# ->
173 if (w2i ((i2w x#) `and#` (i2w (0# -# x#))) /=# x#) then
176 Just (toInteger (iBox (pow2 x#)))
179 pow2 x# | x# ==# 1# = 0#
180 | otherwise = 1# +# pow2 (w2i (i2w x# `shiftr` 1#))
182 #if __GLASGOW_HASKELL__ >= 503
183 shiftr x y = uncheckedShiftRL# x y
185 shiftr x y = shiftRL# x y
189 % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193 #if alpha_TARGET_ARCH
194 = ALWAYS -- For BI (same as BR)
195 | EQQ -- For CMP and BI (NB: "EQ" is a 1.3 Prelude name)
197 | GTT -- For BI only (NB: "GT" is a 1.3 Prelude name)
198 | LE -- For CMP and BI
199 | LTT -- For CMP and BI (NB: "LT" is a 1.3 Prelude name)
201 | NEVER -- For BI (null instruction)
202 | ULE -- For CMP only
203 | ULT -- For CMP only
206 = ALWAYS -- What's really used? ToDo
222 #if sparc_TARGET_ARCH
223 = ALWAYS -- What's really used? ToDo
240 deriving Eq -- to make an assertion work
245 #if alpha_TARGET_ARCH
248 -- | W -- word (2 bytes): UNUSED
250 | L -- longword (4 bytes)
251 | Q -- quadword (8 bytes)
252 -- | FF -- VAX F-style floating pt: UNUSED
253 -- | GF -- VAX G-style floating pt: UNUSED
254 -- | DF -- VAX D-style floating pt: UNUSED
255 -- | SF -- IEEE single-precision floating pt: UNUSED
256 | TF -- IEEE double-precision floating pt
260 | Bu -- byte (unsigned)
262 | Wu -- word (unsigned)
263 | L -- longword (signed)
264 | Lu -- longword (unsigned)
265 | F -- IEEE single-precision floating pt
266 | DF -- IEEE single-precision floating pt
267 | F80 -- Intel 80-bit internal FP format; only used for spilling
269 #if sparc_TARGET_ARCH
271 | Bu -- byte (unsigned)
272 | H -- halfword (signed, 2 bytes)
273 | Hu -- halfword (unsigned, 2 bytes)
274 | W -- word (4 bytes)
275 | F -- IEEE single-precision floating pt
276 | DF -- IEEE single-precision floating pt
279 primRepToSize :: PrimRep -> Size
281 primRepToSize PtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
282 primRepToSize CodePtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
283 primRepToSize DataPtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
284 primRepToSize RetRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
285 primRepToSize CostCentreRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
286 primRepToSize CharRep = IF_ARCH_alpha(L, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
288 primRepToSize Int8Rep = IF_ARCH_alpha(B, IF_ARCH_i386(B, IF_ARCH_sparc(B, )))
289 primRepToSize Int16Rep = IF_ARCH_alpha(err,IF_ARCH_i386(W, IF_ARCH_sparc(H, )))
290 where err = primRepToSize_fail "Int16Rep"
291 primRepToSize Int32Rep = IF_ARCH_alpha(L, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
292 primRepToSize Word8Rep = IF_ARCH_alpha(Bu, IF_ARCH_i386(Bu, IF_ARCH_sparc(Bu, )))
293 primRepToSize Word16Rep = IF_ARCH_alpha(err,IF_ARCH_i386(Wu, IF_ARCH_sparc(Hu, )))
294 where err = primRepToSize_fail "Word16Rep"
295 primRepToSize Word32Rep = IF_ARCH_alpha(L, IF_ARCH_i386(Lu, IF_ARCH_sparc(W, )))
297 primRepToSize IntRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
298 primRepToSize WordRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
299 primRepToSize AddrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
300 primRepToSize FloatRep = IF_ARCH_alpha(TF, IF_ARCH_i386(F, IF_ARCH_sparc(F, )))
301 primRepToSize DoubleRep = IF_ARCH_alpha(TF, IF_ARCH_i386(DF, IF_ARCH_sparc(DF, )))
302 primRepToSize ArrayRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
303 primRepToSize ByteArrayRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
304 primRepToSize PrimPtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
305 primRepToSize WeakPtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
306 primRepToSize ForeignObjRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
307 primRepToSize BCORep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
308 primRepToSize StablePtrRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
309 primRepToSize StableNameRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
310 primRepToSize ThreadIdRep = IF_ARCH_alpha(Q, IF_ARCH_i386(L, IF_ARCH_sparc(W, )))
312 primRepToSize Word64Rep = primRepToSize_fail "Word64Rep"
313 primRepToSize Int64Rep = primRepToSize_fail "Int64Rep"
314 primRepToSize other = primRepToSize_fail (showSDoc (ppr other))
316 primRepToSize_fail str
317 = error ("ERROR: MachMisc.primRepToSize: cannot handle `" ++ str ++ "'.\n\t"
318 ++ "Workaround: use -fvia-C.\n\t"
319 ++ "Perhaps you should report it as a GHC bug,\n\t"
320 ++ "to glasgow-haskell-bugs@haskell.org.")
324 %************************************************************************
326 \subsection{Machine's assembly language}
328 %************************************************************************
330 We have a few common ``instructions'' (nearly all the pseudo-ops) but
331 mostly all of @Instr@ is machine-specific.
335 = COMMENT FAST_STRING -- comment pseudo-op
336 | SEGMENT CodeSegment -- {data,text} segment pseudo-op
337 | LABEL CLabel -- global label pseudo-op
338 | ASCII Bool -- True <=> needs backslash conversion
339 String -- the literal string
342 | DELTA Int -- specify current stack offset for
343 -- benefit of subsequent passes
347 #if alpha_TARGET_ARCH
349 -- data Instr continues...
353 | LD Size Reg MachRegsAddr -- size, dst, src
354 | LDA Reg MachRegsAddr -- dst, src
355 | LDAH Reg MachRegsAddr -- dst, src
356 | LDGP Reg MachRegsAddr -- dst, src
357 | LDI Size Reg Imm -- size, dst, src
358 | ST Size Reg MachRegsAddr -- size, src, dst
363 | ABS Size RI Reg -- size, src, dst
364 | NEG Size Bool RI Reg -- size, overflow, src, dst
365 | ADD Size Bool Reg RI Reg -- size, overflow, src, src, dst
366 | SADD Size Size Reg RI Reg -- size, scale, src, src, dst
367 | SUB Size Bool Reg RI Reg -- size, overflow, src, src, dst
368 | SSUB Size Size Reg RI Reg -- size, scale, src, src, dst
369 | MUL Size Bool Reg RI Reg -- size, overflow, src, src, dst
370 | DIV Size Bool Reg RI Reg -- size, unsigned, src, src, dst
371 | REM Size Bool Reg RI Reg -- size, unsigned, src, src, dst
373 -- Simple bit-twiddling.
393 | CMP Cond Reg RI Reg
400 | FADD Size Reg Reg Reg
401 | FDIV Size Reg Reg Reg
402 | FMUL Size Reg Reg Reg
403 | FSUB Size Reg Reg Reg
404 | CVTxy Size Size Reg Reg
405 | FCMP Size Cond Reg Reg Reg
413 | JMP Reg MachRegsAddr Int
415 | JSR Reg MachRegsAddr Int
417 -- Alpha-specific pseudo-ops.
426 #endif {- alpha_TARGET_ARCH -}
429 Intel, in their infinite wisdom, selected a stack model for floating
430 point registers on x86. That might have made sense back in 1979 --
431 nowadays we can see it for the nonsense it really is. A stack model
432 fits poorly with the existing nativeGen infrastructure, which assumes
433 flat integer and FP register sets. Prior to this commit, nativeGen
434 could not generate correct x86 FP code -- to do so would have meant
435 somehow working the register-stack paradigm into the register
436 allocator and spiller, which sounds very difficult.
438 We have decided to cheat, and go for a simple fix which requires no
439 infrastructure modifications, at the expense of generating ropey but
440 correct FP code. All notions of the x86 FP stack and its insns have
441 been removed. Instead, we pretend (to the instruction selector and
442 register allocator) that x86 has six floating point registers, %fake0
443 .. %fake5, which can be used in the usual flat manner. We further
444 claim that x86 has floating point instructions very similar to SPARC
445 and Alpha, that is, a simple 3-operand register-register arrangement.
446 Code generation and register allocation proceed on this basis.
448 When we come to print out the final assembly, our convenient fiction
449 is converted to dismal reality. Each fake instruction is
450 independently converted to a series of real x86 instructions.
451 %fake0 .. %fake5 are mapped to %st(0) .. %st(5). To do reg-reg
452 arithmetic operations, the two operands are pushed onto the top of the
453 FP stack, the operation done, and the result copied back into the
454 relevant register. There are only six %fake registers because 2 are
455 needed for the translation, and x86 has 8 in total.
457 The translation is inefficient but is simple and it works. A cleverer
458 translation would handle a sequence of insns, simulating the FP stack
459 contents, would not impose a fixed mapping from %fake to %st regs, and
460 hopefully could avoid most of the redundant reg-reg moves of the
463 We might as well make use of whatever unique FP facilities Intel have
464 chosen to bless us with (let's not be churlish, after all).
465 Hence GLDZ and GLD1. Bwahahahahahahaha!
467 LATER (10 Nov 2000): idiv gives problems with the register spiller,
468 because the spiller is simpleminded and because idiv has fixed uses of
469 %eax and %edx. Rather than make the spiller cleverer, we do away with
470 idiv, and instead have iquot and irem fake (integer) insns, which have
471 no operand register constraints -- ie, they behave like add, sub, mul.
472 The printer-outer transforms them to a sequence of real insns which does
473 the Right Thing (tm). As with the FP stuff, this gives ropey code,
474 but we don't care, since it doesn't get used much. We hope.
479 -- data Instr continues...
483 | MOV Size Operand Operand
484 | MOVZxL Size Operand Operand -- size is the size of operand 1
485 | MOVSxL Size Operand Operand -- size is the size of operand 1
487 -- Load effective address (also a very useful three-operand add instruction :-)
489 | LEA Size Operand Operand
493 | ADD Size Operand Operand
494 | SUB Size Operand Operand
495 | IMUL Size Operand Operand -- signed int mul
496 | MUL Size Operand Operand -- unsigned int mul
497 | IMUL64 Reg Reg -- 32 x 32 -> 64 signed mul
498 -- operand1:operand2 := (operand1[31:0] *signed operand2[31:0])
500 -- Quotient and remainder. SEE comment above -- these are not
501 -- real x86 insns; instead they are expanded when printed
502 -- into a sequence of real insns.
504 | IQUOT Size Operand Operand -- signed quotient
505 | IREM Size Operand Operand -- signed remainder
506 | QUOT Size Operand Operand -- unsigned quotient
507 | REM Size Operand Operand -- unsigned remainder
509 -- Simple bit-twiddling.
511 | AND Size Operand Operand
512 | OR Size Operand Operand
513 | XOR Size Operand Operand
515 | NEGI Size Operand -- NEG instruction (name clash with Cond)
516 | SHL Size Imm Operand -- Only immediate shifts allowed
517 | SAR Size Imm Operand -- Only immediate shifts allowed
518 | SHR Size Imm Operand -- Only immediate shifts allowed
519 | BT Size Imm Operand
524 -- Note that we cheat by treating G{ABS,MOV,NEG} of doubles
525 -- as single instructions right up until we spit them out.
527 -- all the 3-operand fake fp insns are src1 src2 dst
528 -- and furthermore are constrained to be fp regs only.
529 -- IMPORTANT: keep is_G_insn up to date with any changes here
530 | GMOV Reg Reg -- src(fpreg), dst(fpreg)
531 | GLD Size MachRegsAddr Reg -- src, dst(fpreg)
532 | GST Size Reg MachRegsAddr -- src(fpreg), dst
534 | GLDZ Reg -- dst(fpreg)
535 | GLD1 Reg -- dst(fpreg)
537 | GFTOI Reg Reg -- src(fpreg), dst(intreg)
538 | GDTOI Reg Reg -- src(fpreg), dst(intreg)
540 | GITOF Reg Reg -- src(intreg), dst(fpreg)
541 | GITOD Reg Reg -- src(intreg), dst(fpreg)
543 | GADD Size Reg Reg Reg -- src1, src2, dst
544 | GDIV Size Reg Reg Reg -- src1, src2, dst
545 | GSUB Size Reg Reg Reg -- src1, src2, dst
546 | GMUL Size Reg Reg Reg -- src1, src2, dst
548 -- FP compare. Cond must be `elem` [EQQ, NE, LE, LTT, GE, GTT]
549 -- Compare src1 with src2; set the Zero flag iff the numbers are
550 -- comparable and the comparison is True. Subsequent code must
551 -- test the %eflags zero flag regardless of the supplied Cond.
552 | GCMP Cond Reg Reg -- src1, src2
554 | GABS Size Reg Reg -- src, dst
555 | GNEG Size Reg Reg -- src, dst
556 | GSQRT Size Reg Reg -- src, dst
557 | GSIN Size Reg Reg -- src, dst
558 | GCOS Size Reg Reg -- src, dst
559 | GTAN Size Reg Reg -- src, dst
561 | GFREE -- do ffree on all x86 regs; an ugly hack
564 | TEST Size Operand Operand
565 | CMP Size Operand Operand
577 | JMP DestInfo Operand -- possible dests, target
578 | JXX Cond CLabel -- target
583 | CLTD -- sign extend %eax into %edx:%eax
586 = OpReg Reg -- register
587 | OpImm Imm -- immediate value
588 | OpAddr MachRegsAddr -- memory reference
591 i386_insert_ffrees :: [Instr] -> [Instr]
592 i386_insert_ffrees insns
593 | any is_G_instr insns
594 = concatMap ffree_before_nonlocal_transfers insns
598 ffree_before_nonlocal_transfers insn
600 CALL _ -> [GFREE, insn]
601 -- Jumps to immediate labels are local
602 JMP _ (OpImm (ImmCLbl clbl)) | isAsmTemp clbl -> [insn]
603 -- If a jump mentions dests, it is a local jump thru
605 JMP (DestInfo _) _ -> [insn]
606 JMP _ _ -> [GFREE, insn]
610 -- if you ever add a new FP insn to the fake x86 FP insn set,
611 -- you must update this too
612 is_G_instr :: Instr -> Bool
615 GMOV _ _ -> True; GLD _ _ _ -> True; GST _ _ _ -> True;
616 GLDZ _ -> True; GLD1 _ -> True;
617 GFTOI _ _ -> True; GDTOI _ _ -> True;
618 GITOF _ _ -> True; GITOD _ _ -> True;
619 GADD _ _ _ _ -> True; GDIV _ _ _ _ -> True
620 GSUB _ _ _ _ -> True; GMUL _ _ _ _ -> True
621 GCMP _ _ _ -> True; GABS _ _ _ -> True
622 GNEG _ _ _ -> True; GSQRT _ _ _ -> True
623 GSIN _ _ _ -> True; GCOS _ _ _ -> True; GTAN _ _ _ -> True;
624 GFREE -> panic "is_G_instr: GFREE (!)"
627 #endif {- i386_TARGET_ARCH -}
631 #if sparc_TARGET_ARCH
633 -- data Instr continues...
637 | LD Size MachRegsAddr Reg -- size, src, dst
638 | ST Size Reg MachRegsAddr -- size, src, dst
642 | ADD Bool Bool Reg RI Reg -- x?, cc?, src1, src2, dst
643 | SUB Bool Bool Reg RI Reg -- x?, cc?, src1, src2, dst
644 | UMUL Bool Reg RI Reg -- cc?, src1, src2, dst
645 | SMUL Bool Reg RI Reg -- cc?, src1, src2, dst
646 | RDY Reg -- move contents of Y register to reg
648 -- Simple bit-twiddling.
650 | AND Bool Reg RI Reg -- cc?, src1, src2, dst
651 | ANDN Bool Reg RI Reg -- cc?, src1, src2, dst
652 | OR Bool Reg RI Reg -- cc?, src1, src2, dst
653 | ORN Bool Reg RI Reg -- cc?, src1, src2, dst
654 | XOR Bool Reg RI Reg -- cc?, src1, src2, dst
655 | XNOR Bool Reg RI Reg -- cc?, src1, src2, dst
656 | SLL Reg RI Reg -- src1, src2, dst
657 | SRL Reg RI Reg -- src1, src2, dst
658 | SRA Reg RI Reg -- src1, src2, dst
659 | SETHI Imm Reg -- src, dst
660 | NOP -- Really SETHI 0, %g0, but worth an alias
664 -- Note that we cheat by treating F{ABS,MOV,NEG} of doubles as single instructions
665 -- right up until we spit them out.
667 | FABS Size Reg Reg -- src dst
668 | FADD Size Reg Reg Reg -- src1, src2, dst
669 | FCMP Bool Size Reg Reg -- exception?, src1, src2, dst
670 | FDIV Size Reg Reg Reg -- src1, src2, dst
671 | FMOV Size Reg Reg -- src, dst
672 | FMUL Size Reg Reg Reg -- src1, src2, dst
673 | FNEG Size Reg Reg -- src, dst
674 | FSQRT Size Reg Reg -- src, dst
675 | FSUB Size Reg Reg Reg -- src1, src2, dst
676 | FxTOy Size Size Reg Reg -- src, dst
680 | BI Cond Bool Imm -- cond, annul?, target
681 | BF Cond Bool Imm -- cond, annul?, target
683 | JMP DestInfo MachRegsAddr -- target
684 | CALL Imm Int Bool -- target, args, terminal
691 riZero (RIImm (ImmInt 0)) = True
692 riZero (RIImm (ImmInteger 0)) = True
693 riZero (RIReg (RealReg 0)) = True
696 -- Calculate the effective address which would be used by the
697 -- corresponding fpRel sequence. fpRel is in MachRegs.lhs,
698 -- alas -- can't have fpRelEA here because of module dependencies.
699 fpRelEA :: Int -> Reg -> Instr
701 = ADD False False fp (RIImm (ImmInt (n * BYTES_PER_WORD))) dst
703 -- Code to shift the stack pointer by n words.
704 moveSp :: Int -> Instr
706 = ADD False False sp (RIImm (ImmInt (n * BYTES_PER_WORD))) sp
708 -- Produce the second-half-of-a-double register given the first half.
710 fPair (RealReg n) | n >= 32 && n `mod` 2 == 0 = RealReg (n+1)
711 fPair other = pprPanic "fPair(sparc NCG)" (ppr other)
712 #endif {- sparc_TARGET_ARCH -}