2 -- The above warning supression flag is a temporary kludge.
3 -- While working on this module you are encouraged to remove it and fix
4 -- any warnings in the module. See
5 -- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
8 -----------------------------------------------------------------------------
10 -- Machine-dependent assembly language
12 -- (c) The University of Glasgow 1993-2004
14 -----------------------------------------------------------------------------
16 #include "nativeGen/NCG.h"
19 -- * Cmm instantiations
20 NatCmm, NatCmmTop, NatBasicBlock,
22 -- * Machine instructions
24 Cond(..), condUnsigned, condToSigned, condToUnsigned,
25 #if powerpc_TARGET_ARCH
28 #if !powerpc_TARGET_ARCH && !i386_TARGET_ARCH && !x86_64_TARGET_ARCH
29 Size(..), machRepSize,
33 #if i386_TARGET_ARCH || x86_64_TARGET_ARCH
40 riZero, fpRelEA, moveSp, fPair,
44 #include "HsVersions.h"
49 import MachOp ( MachRep(..) )
50 import CLabel ( CLabel, pprCLabel )
51 import Panic ( panic )
54 import Constants ( wORD_SIZE )
59 -- -----------------------------------------------------------------------------
60 -- Our flavours of the Cmm types
62 -- Type synonyms for Cmm populated with native code
63 type NatCmm = GenCmm CmmStatic [CmmStatic] (ListGraph Instr)
64 type NatCmmTop = GenCmmTop CmmStatic [CmmStatic] (ListGraph Instr)
65 type NatBasicBlock = GenBasicBlock Instr
67 -- -----------------------------------------------------------------------------
68 -- Conditions on this architecture
72 = ALWAYS -- For BI (same as BR)
73 | EQQ -- For CMP and BI (NB: "EQ" is a 1.3 Prelude name)
75 | GTT -- For BI only (NB: "GT" is a 1.3 Prelude name)
76 | LE -- For CMP and BI
77 | LTT -- For CMP and BI (NB: "LT" is a 1.3 Prelude name)
79 | NEVER -- For BI (null instruction)
83 #if i386_TARGET_ARCH || x86_64_TARGET_ARCH
84 = ALWAYS -- What's really used? ToDo
102 #if sparc_TARGET_ARCH
103 = ALWAYS -- What's really used? ToDo
120 #if powerpc_TARGET_ARCH
133 deriving Eq -- to make an assertion work
135 condUnsigned GU = True
136 condUnsigned LU = True
137 condUnsigned GEU = True
138 condUnsigned LEU = True
139 condUnsigned _ = False
141 condToSigned GU = GTT
142 condToSigned LU = LTT
143 condToSigned GEU = GE
144 condToSigned LEU = LE
147 condToUnsigned GTT = GU
148 condToUnsigned LTT = LU
149 condToUnsigned GE = GEU
150 condToUnsigned LE = LEU
153 #if powerpc_TARGET_ARCH
154 condNegate ALWAYS = panic "condNegate: ALWAYS"
167 -- -----------------------------------------------------------------------------
168 -- Sizes on this architecture
170 -- ToDo: it's not clear to me that we need separate signed-vs-unsigned sizes
171 -- here. I've removed them from the x86 version, we'll see what happens --SDM
173 #if !powerpc_TARGET_ARCH && !i386_TARGET_ARCH && !x86_64_TARGET_ARCH
175 #if alpha_TARGET_ARCH
178 -- | W -- word (2 bytes): UNUSED
180 | L -- longword (4 bytes)
181 | Q -- quadword (8 bytes)
182 -- | FF -- VAX F-style floating pt: UNUSED
183 -- | GF -- VAX G-style floating pt: UNUSED
184 -- | DF -- VAX D-style floating pt: UNUSED
185 -- | SF -- IEEE single-precision floating pt: UNUSED
186 | TF -- IEEE double-precision floating pt
188 #if sparc_TARGET_ARCH || powerpc_TARGET_ARCH
190 | Bu -- byte (unsigned)
191 | H -- halfword (signed, 2 bytes)
192 | Hu -- halfword (unsigned, 2 bytes)
193 | W -- word (4 bytes)
194 | F -- IEEE single-precision floating pt
195 | DF -- IEEE single-precision floating pt
199 machRepSize :: MachRep -> Size
200 machRepSize I8 = IF_ARCH_alpha(Bu, IF_ARCH_sparc(Bu, ))
201 machRepSize I16 = IF_ARCH_alpha(err,IF_ARCH_sparc(Hu, ))
202 machRepSize I32 = IF_ARCH_alpha(L, IF_ARCH_sparc(W, ))
203 machRepSize I64 = panic "machRepSize: I64"
204 machRepSize I128 = panic "machRepSize: I128"
205 machRepSize F32 = IF_ARCH_alpha(TF, IF_ARCH_sparc(F, ))
206 machRepSize F64 = IF_ARCH_alpha(TF, IF_ARCH_sparc(DF,))
209 -- -----------------------------------------------------------------------------
210 -- Register or immediate (a handy type on some platforms)
216 -- -----------------------------------------------------------------------------
217 -- Machine's assembly language
219 -- We have a few common "instructions" (nearly all the pseudo-ops) but
220 -- mostly all of 'Instr' is machine-specific.
223 = COMMENT FastString -- comment pseudo-op
225 | LDATA Section [CmmStatic] -- some static data spat out during code
226 -- generation. Will be extracted before
229 | NEWBLOCK BlockId -- start a new basic block. Useful during
230 -- codegen, removed later. Preceding
231 -- instruction should be a jump, as per the
232 -- invariants for a BasicBlock (see Cmm).
234 | DELTA Int -- specify current stack offset for
235 -- benefit of subsequent passes
237 | SPILL Reg Int -- ^ spill this reg to a stack slot
238 | RELOAD Int Reg -- ^ reload this reg from a stack slot
240 -- -----------------------------------------------------------------------------
241 -- Alpha instructions
243 #if alpha_TARGET_ARCH
245 -- data Instr continues...
248 | LD Size Reg AddrMode -- size, dst, src
249 | LDA Reg AddrMode -- dst, src
250 | LDAH Reg AddrMode -- dst, src
251 | LDGP Reg AddrMode -- dst, src
252 | LDI Size Reg Imm -- size, dst, src
253 | ST Size Reg AddrMode -- size, src, dst
257 | ABS Size RI Reg -- size, src, dst
258 | NEG Size Bool RI Reg -- size, overflow, src, dst
259 | ADD Size Bool Reg RI Reg -- size, overflow, src, src, dst
260 | SADD Size Size Reg RI Reg -- size, scale, src, src, dst
261 | SUB Size Bool Reg RI Reg -- size, overflow, src, src, dst
262 | SSUB Size Size Reg RI Reg -- size, scale, src, src, dst
263 | MUL Size Bool Reg RI Reg -- size, overflow, src, src, dst
264 | DIV Size Bool Reg RI Reg -- size, unsigned, src, src, dst
265 | REM Size Bool Reg RI Reg -- size, unsigned, src, src, dst
267 -- Simple bit-twiddling.
285 | CMP Cond Reg RI Reg
291 | FADD Size Reg Reg Reg
292 | FDIV Size Reg Reg Reg
293 | FMUL Size Reg Reg Reg
294 | FSUB Size Reg Reg Reg
295 | CVTxy Size Size Reg Reg
296 | FCMP Size Cond Reg Reg Reg
303 | JMP Reg AddrMode Int
305 | JSR Reg AddrMode Int
307 -- Alpha-specific pseudo-ops.
315 #endif /* alpha_TARGET_ARCH */
318 -- -----------------------------------------------------------------------------
319 -- Intel x86 instructions
322 Intel, in their infinite wisdom, selected a stack model for floating
323 point registers on x86. That might have made sense back in 1979 --
324 nowadays we can see it for the nonsense it really is. A stack model
325 fits poorly with the existing nativeGen infrastructure, which assumes
326 flat integer and FP register sets. Prior to this commit, nativeGen
327 could not generate correct x86 FP code -- to do so would have meant
328 somehow working the register-stack paradigm into the register
329 allocator and spiller, which sounds very difficult.
331 We have decided to cheat, and go for a simple fix which requires no
332 infrastructure modifications, at the expense of generating ropey but
333 correct FP code. All notions of the x86 FP stack and its insns have
334 been removed. Instead, we pretend (to the instruction selector and
335 register allocator) that x86 has six floating point registers, %fake0
336 .. %fake5, which can be used in the usual flat manner. We further
337 claim that x86 has floating point instructions very similar to SPARC
338 and Alpha, that is, a simple 3-operand register-register arrangement.
339 Code generation and register allocation proceed on this basis.
341 When we come to print out the final assembly, our convenient fiction
342 is converted to dismal reality. Each fake instruction is
343 independently converted to a series of real x86 instructions.
344 %fake0 .. %fake5 are mapped to %st(0) .. %st(5). To do reg-reg
345 arithmetic operations, the two operands are pushed onto the top of the
346 FP stack, the operation done, and the result copied back into the
347 relevant register. There are only six %fake registers because 2 are
348 needed for the translation, and x86 has 8 in total.
350 The translation is inefficient but is simple and it works. A cleverer
351 translation would handle a sequence of insns, simulating the FP stack
352 contents, would not impose a fixed mapping from %fake to %st regs, and
353 hopefully could avoid most of the redundant reg-reg moves of the
356 We might as well make use of whatever unique FP facilities Intel have
357 chosen to bless us with (let's not be churlish, after all).
358 Hence GLDZ and GLD1. Bwahahahahahahaha!
362 MORE FLOATING POINT MUSINGS...
364 Intel's internal floating point registers are by default 80 bit
365 extended precision. This means that all operations done on values in
366 registers are done at 80 bits, and unless the intermediate values are
367 truncated to the appropriate size (32 or 64 bits) by storing in
368 memory, calculations in registers will give different results from
369 calculations which pass intermediate values in memory (eg. via
372 One solution is to set the FPU into 64 bit precision mode. Some OSs
373 do this (eg. FreeBSD) and some don't (eg. Linux). The problem here is
374 that this will only affect 64-bit precision arithmetic; 32-bit
375 calculations will still be done at 64-bit precision in registers. So
376 it doesn't solve the whole problem.
378 There's also the issue of what the C library is expecting in terms of
379 precision. It seems to be the case that glibc on Linux expects the
380 FPU to be set to 80 bit precision, so setting it to 64 bit could have
381 unexpected effects. Changing the default could have undesirable
382 effects on other 3rd-party library code too, so the right thing would
383 be to save/restore the FPU control word across Haskell code if we were
386 gcc's -ffloat-store gives consistent results by always storing the
387 results of floating-point calculations in memory, which works for both
388 32 and 64-bit precision. However, it only affects the values of
389 user-declared floating point variables in C, not intermediate results.
390 GHC in -fvia-C mode uses -ffloat-store (see the -fexcess-precision
393 Another problem is how to spill floating point registers in the
394 register allocator. Should we spill the whole 80 bits, or just 64?
395 On an OS which is set to 64 bit precision, spilling 64 is fine. On
396 Linux, spilling 64 bits will round the results of some operations.
397 This is what gcc does. Spilling at 80 bits requires taking up a full
398 128 bit slot (so we get alignment). We spill at 80-bits and ignore
399 the alignment problems.
401 In the future, we'll use the SSE registers for floating point. This
402 requires a CPU that supports SSE2 (ordinary SSE only supports 32 bit
403 precision float ops), which means P4 or Xeon and above. Using SSE
404 will solve all these problems, because the SSE registers use fixed 32
405 bit or 64 bit precision.
410 #if i386_TARGET_ARCH || x86_64_TARGET_ARCH
412 -- data Instr continues...
415 | MOV MachRep Operand Operand
416 | MOVZxL MachRep Operand Operand -- size is the size of operand 1
417 | MOVSxL MachRep Operand Operand -- size is the size of operand 1
418 -- x86_64 note: plain mov into a 32-bit register always zero-extends
419 -- into the 64-bit reg, in contrast to the 8 and 16-bit movs which
420 -- don't affect the high bits of the register.
422 -- Load effective address (also a very useful three-operand add instruction :-)
423 | LEA MachRep Operand Operand
426 | ADD MachRep Operand Operand
427 | ADC MachRep Operand Operand
428 | SUB MachRep Operand Operand
430 | MUL MachRep Operand Operand
431 | IMUL MachRep Operand Operand -- signed int mul
432 | IMUL2 MachRep Operand -- %edx:%eax = operand * %eax
434 | DIV MachRep Operand -- eax := eax:edx/op, edx := eax:edx%op
435 | IDIV MachRep Operand -- ditto, but signed
437 -- Simple bit-twiddling.
438 | AND MachRep Operand Operand
439 | OR MachRep Operand Operand
440 | XOR MachRep Operand Operand
441 | NOT MachRep Operand
442 | NEGI MachRep Operand -- NEG instruction (name clash with Cond)
444 -- Shifts (amount may be immediate or %cl only)
445 | SHL MachRep Operand{-amount-} Operand
446 | SAR MachRep Operand{-amount-} Operand
447 | SHR MachRep Operand{-amount-} Operand
449 | BT MachRep Imm Operand
455 -- Note that we cheat by treating G{ABS,MOV,NEG} of doubles
456 -- as single instructions right up until we spit them out.
457 -- all the 3-operand fake fp insns are src1 src2 dst
458 -- and furthermore are constrained to be fp regs only.
459 -- IMPORTANT: keep is_G_insn up to date with any changes here
460 | GMOV Reg Reg -- src(fpreg), dst(fpreg)
461 | GLD MachRep AddrMode Reg -- src, dst(fpreg)
462 | GST MachRep Reg AddrMode -- src(fpreg), dst
464 | GLDZ Reg -- dst(fpreg)
465 | GLD1 Reg -- dst(fpreg)
467 | GFTOI Reg Reg -- src(fpreg), dst(intreg)
468 | GDTOI Reg Reg -- src(fpreg), dst(intreg)
470 | GITOF Reg Reg -- src(intreg), dst(fpreg)
471 | GITOD Reg Reg -- src(intreg), dst(fpreg)
473 | GADD MachRep Reg Reg Reg -- src1, src2, dst
474 | GDIV MachRep Reg Reg Reg -- src1, src2, dst
475 | GSUB MachRep Reg Reg Reg -- src1, src2, dst
476 | GMUL MachRep Reg Reg Reg -- src1, src2, dst
478 -- FP compare. Cond must be `elem` [EQQ, NE, LE, LTT, GE, GTT]
479 -- Compare src1 with src2; set the Zero flag iff the numbers are
480 -- comparable and the comparison is True. Subsequent code must
481 -- test the %eflags zero flag regardless of the supplied Cond.
482 | GCMP Cond Reg Reg -- src1, src2
484 | GABS MachRep Reg Reg -- src, dst
485 | GNEG MachRep Reg Reg -- src, dst
486 | GSQRT MachRep Reg Reg -- src, dst
487 | GSIN MachRep CLabel CLabel Reg Reg -- src, dst
488 | GCOS MachRep CLabel CLabel Reg Reg -- src, dst
489 | GTAN MachRep CLabel CLabel Reg Reg -- src, dst
491 | GFREE -- do ffree on all x86 regs; an ugly hack
494 #if x86_64_TARGET_ARCH
495 -- SSE2 floating point: we use a restricted set of the available SSE2
496 -- instructions for floating-point.
498 -- use MOV for moving (either movss or movsd (movlpd better?))
500 | CVTSS2SD Reg Reg -- F32 to F64
501 | CVTSD2SS Reg Reg -- F64 to F32
502 | CVTTSS2SIQ Operand Reg -- F32 to I32/I64 (with truncation)
503 | CVTTSD2SIQ Operand Reg -- F64 to I32/I64 (with truncation)
504 | CVTSI2SS Operand Reg -- I32/I64 to F32
505 | CVTSI2SD Operand Reg -- I32/I64 to F64
507 -- use ADD & SUB for arithmetic. In both cases, operands
510 -- SSE2 floating-point division:
511 | FDIV MachRep Operand Operand -- divisor, dividend(dst)
513 -- use CMP for comparisons. ucomiss and ucomisd instructions
514 -- compare single/double prec floating point respectively.
516 | SQRT MachRep Operand Reg -- src, dst
520 | TEST MachRep Operand Operand
521 | CMP MachRep Operand Operand
525 | PUSH MachRep Operand
526 | POP MachRep Operand
527 -- both unused (SDM):
533 | JXX Cond BlockId -- includes unconditional branches
534 | JXX_GBL Cond Imm -- non-local version of JXX
535 | JMP_TBL Operand [BlockId] -- table jump
536 | CALL (Either Imm Reg) [Reg]
539 | CLTD MachRep -- sign extend %eax into %edx:%eax
541 | FETCHGOT Reg -- pseudo-insn for ELF position-independent code
545 -- addl __GLOBAL_OFFSET_TABLE__+.-1b, %reg
546 | FETCHPC Reg -- pseudo-insn for Darwin position-independent code
553 = OpReg Reg -- register
554 | OpImm Imm -- immediate value
555 | OpAddr AddrMode -- memory reference
557 #endif /* i386 or x86_64 */
560 i386_insert_ffrees :: [Instr] -> [Instr]
561 i386_insert_ffrees insns
562 | any is_G_instr insns
563 = concatMap ffree_before_nonlocal_transfers insns
567 ffree_before_nonlocal_transfers insn
569 CALL _ _ -> [GFREE, insn]
570 JMP _ -> [GFREE, insn]
574 -- if you ever add a new FP insn to the fake x86 FP insn set,
575 -- you must update this too
576 is_G_instr :: Instr -> Bool
579 GMOV _ _ -> True; GLD _ _ _ -> True; GST _ _ _ -> True
580 GLDZ _ -> True; GLD1 _ -> True
581 GFTOI _ _ -> True; GDTOI _ _ -> True
582 GITOF _ _ -> True; GITOD _ _ -> True
583 GADD _ _ _ _ -> True; GDIV _ _ _ _ -> True
584 GSUB _ _ _ _ -> True; GMUL _ _ _ _ -> True
585 GCMP _ _ _ -> True; GABS _ _ _ -> True
586 GNEG _ _ _ -> True; GSQRT _ _ _ -> True
587 GSIN _ _ _ _ _ -> True; GCOS _ _ _ _ _ -> True; GTAN _ _ _ _ _ -> True
588 GFREE -> panic "is_G_instr: GFREE (!)"
590 #endif /* i386_TARGET_ARCH */
593 -- -----------------------------------------------------------------------------
594 -- Sparc instructions
596 #if sparc_TARGET_ARCH
598 -- data Instr continues...
601 | LD MachRep AddrMode Reg -- size, src, dst
602 | ST MachRep Reg AddrMode -- size, src, dst
605 | ADD Bool Bool Reg RI Reg -- x?, cc?, src1, src2, dst
606 | SUB Bool Bool Reg RI Reg -- x?, cc?, src1, src2, dst
607 | UMUL Bool Reg RI Reg -- cc?, src1, src2, dst
608 | SMUL Bool Reg RI Reg -- cc?, src1, src2, dst
609 | RDY Reg -- move contents of Y register to reg
611 -- Simple bit-twiddling.
612 | AND Bool Reg RI Reg -- cc?, src1, src2, dst
613 | ANDN Bool Reg RI Reg -- cc?, src1, src2, dst
614 | OR Bool Reg RI Reg -- cc?, src1, src2, dst
615 | ORN Bool Reg RI Reg -- cc?, src1, src2, dst
616 | XOR Bool Reg RI Reg -- cc?, src1, src2, dst
617 | XNOR Bool Reg RI Reg -- cc?, src1, src2, dst
618 | SLL Reg RI Reg -- src1, src2, dst
619 | SRL Reg RI Reg -- src1, src2, dst
620 | SRA Reg RI Reg -- src1, src2, dst
621 | SETHI Imm Reg -- src, dst
622 | NOP -- Really SETHI 0, %g0, but worth an alias
626 -- Note that we cheat by treating F{ABS,MOV,NEG} of doubles as single
627 -- instructions right up until we spit them out.
628 | FABS MachRep Reg Reg -- src dst
629 | FADD MachRep Reg Reg Reg -- src1, src2, dst
630 | FCMP Bool MachRep Reg Reg -- exception?, src1, src2, dst
631 | FDIV MachRep Reg Reg Reg -- src1, src2, dst
632 | FMOV MachRep Reg Reg -- src, dst
633 | FMUL MachRep Reg Reg Reg -- src1, src2, dst
634 | FNEG MachRep Reg Reg -- src, dst
635 | FSQRT MachRep Reg Reg -- src, dst
636 | FSUB MachRep Reg Reg Reg -- src1, src2, dst
637 | FxTOy MachRep MachRep Reg Reg -- src, dst
640 | BI Cond Bool Imm -- cond, annul?, target
641 | BF Cond Bool Imm -- cond, annul?, target
643 | JMP AddrMode -- target
644 | CALL (Either Imm Reg) Int Bool -- target, args, terminal
648 riZero (RIImm (ImmInt 0)) = True
649 riZero (RIImm (ImmInteger 0)) = True
650 riZero (RIReg (RealReg 0)) = True
653 -- Calculate the effective address which would be used by the
654 -- corresponding fpRel sequence. fpRel is in MachRegs.lhs,
655 -- alas -- can't have fpRelEA here because of module dependencies.
656 fpRelEA :: Int -> Reg -> Instr
658 = ADD False False fp (RIImm (ImmInt (n * wORD_SIZE))) dst
660 -- Code to shift the stack pointer by n words.
661 moveSp :: Int -> Instr
663 = ADD False False sp (RIImm (ImmInt (n * wORD_SIZE))) sp
665 -- Produce the second-half-of-a-double register given the first half.
667 fPair (RealReg n) | n >= 32 && n `mod` 2 == 0 = RealReg (n+1)
668 fPair other = pprPanic "fPair(sparc NCG)" (ppr other)
669 #endif /* sparc_TARGET_ARCH */
672 -- -----------------------------------------------------------------------------
673 -- PowerPC instructions
675 #ifdef powerpc_TARGET_ARCH
676 -- data Instr continues...
679 | LD MachRep Reg AddrMode -- Load size, dst, src
680 | LA MachRep Reg AddrMode -- Load arithmetic size, dst, src
681 | ST MachRep Reg AddrMode -- Store size, src, dst
682 | STU MachRep Reg AddrMode -- Store with Update size, src, dst
683 | LIS Reg Imm -- Load Immediate Shifted dst, src
684 | LI Reg Imm -- Load Immediate dst, src
685 | MR Reg Reg -- Move Register dst, src -- also for fmr
687 | CMP MachRep Reg RI --- size, src1, src2
688 | CMPL MachRep Reg RI --- size, src1, src2
691 | BCCFAR Cond BlockId
692 | JMP CLabel -- same as branch,
693 -- but with CLabel instead of block ID
695 | BCTR [BlockId] -- with list of local destinations
696 | BL CLabel [Reg] -- with list of argument regs
699 | ADD Reg Reg RI -- dst, src1, src2
700 | ADDC Reg Reg Reg -- (carrying) dst, src1, src2
701 | ADDE Reg Reg Reg -- (extend) dst, src1, src2
702 | ADDIS Reg Reg Imm -- Add Immediate Shifted dst, src1, src2
703 | SUBF Reg Reg Reg -- dst, src1, src2 ; dst = src2 - src1
708 | MULLW_MayOflo Reg Reg Reg
709 -- dst = 1 if src1 * src2 overflows
710 -- pseudo-instruction; pretty-printed as:
711 -- mullwo. dst, src1, src2
713 -- rlwinm dst, dst, 2, 31,31
715 | AND Reg Reg RI -- dst, src1, src2
716 | OR Reg Reg RI -- dst, src1, src2
717 | XOR Reg Reg RI -- dst, src1, src2
718 | XORIS Reg Reg Imm -- XOR Immediate Shifted dst, src1, src2
720 | EXTS MachRep Reg Reg
725 | SLW Reg Reg RI -- shift left word
726 | SRW Reg Reg RI -- shift right word
727 | SRAW Reg Reg RI -- shift right arithmetic word
729 -- Rotate Left Word Immediate then AND with Mask
730 | RLWINM Reg Reg Int Int Int
732 | FADD MachRep Reg Reg Reg
733 | FSUB MachRep Reg Reg Reg
734 | FMUL MachRep Reg Reg Reg
735 | FDIV MachRep Reg Reg Reg
736 | FNEG Reg Reg -- negate is the same for single and double prec.
740 | FCTIWZ Reg Reg -- convert to integer word
741 | FRSP Reg Reg -- reduce to single precision
742 -- (but destination is a FP register)
744 | CRNOR Int Int Int -- condition register nor
745 | MFCR Reg -- move from condition register
747 | MFLR Reg -- move from link register
748 | FETCHPC Reg -- pseudo-instruction:
749 -- bcl to next insn, mflr reg
751 | LWSYNC -- memory barrier
752 #endif /* powerpc_TARGET_ARCH */