----------------------------------------------------------------------------- -- -- Machine-dependent assembly language -- -- (c) The University of Glasgow 1993-2004 -- ----------------------------------------------------------------------------- #include "HsVersions.h" #include "nativeGen/NCG.h" module PPC.Instr ( archWordSize, RI(..), Instr(..), maxSpillSlots ) where import PPC.Regs import PPC.Cond import Instruction import Size import TargetReg import RegClass import Reg import Constants (rESERVED_C_STACK_BYTES) import BlockId import Cmm import FastString import CLabel import Outputable import FastBool -------------------------------------------------------------------------------- -- Size of a PPC memory address, in bytes. -- archWordSize :: Size archWordSize = II32 -- | Instruction instance for powerpc instance Instruction Instr where regUsageOfInstr = ppc_regUsageOfInstr patchRegsOfInstr = ppc_patchRegsOfInstr isJumpishInstr = ppc_isJumpishInstr jumpDestsOfInstr = ppc_jumpDestsOfInstr patchJumpInstr = ppc_patchJumpInstr mkSpillInstr = ppc_mkSpillInstr mkLoadInstr = ppc_mkLoadInstr takeDeltaInstr = ppc_takeDeltaInstr isMetaInstr = ppc_isMetaInstr mkRegRegMoveInstr = ppc_mkRegRegMoveInstr takeRegRegMoveInstr = ppc_takeRegRegMoveInstr mkJumpInstr = ppc_mkJumpInstr -- ----------------------------------------------------------------------------- -- Machine's assembly language -- We have a few common "instructions" (nearly all the pseudo-ops) but -- mostly all of 'Instr' is machine-specific. -- Register or immediate data RI = RIReg Reg | RIImm Imm data Instr -- comment pseudo-op = COMMENT FastString -- some static data spat out during code -- generation. Will be extracted before -- pretty-printing. | LDATA Section [CmmStatic] -- start a new basic block. Useful during -- codegen, removed later. Preceding -- instruction should be a jump, as per the -- invariants for a BasicBlock (see Cmm). | NEWBLOCK BlockId -- specify current stack offset for -- benefit of subsequent passes | DELTA Int -- Loads and stores. | LD Size Reg AddrMode -- Load size, dst, src | LA Size Reg AddrMode -- Load arithmetic size, dst, src | ST Size Reg AddrMode -- Store size, src, dst | STU Size Reg AddrMode -- Store with Update size, src, dst | LIS Reg Imm -- Load Immediate Shifted dst, src | LI Reg Imm -- Load Immediate dst, src | MR Reg Reg -- Move Register dst, src -- also for fmr | CMP Size Reg RI --- size, src1, src2 | CMPL Size Reg RI --- size, src1, src2 | BCC Cond BlockId | BCCFAR Cond BlockId | JMP CLabel -- same as branch, -- but with CLabel instead of block ID | MTCTR Reg | BCTR [BlockId] -- with list of local destinations | BL CLabel [Reg] -- with list of argument regs | BCTRL [Reg] | ADD Reg Reg RI -- dst, src1, src2 | ADDC Reg Reg Reg -- (carrying) dst, src1, src2 | ADDE Reg Reg Reg -- (extend) dst, src1, src2 | ADDIS Reg Reg Imm -- Add Immediate Shifted dst, src1, src2 | SUBF Reg Reg Reg -- dst, src1, src2 ; dst = src2 - src1 | MULLW Reg Reg RI | DIVW Reg Reg Reg | DIVWU Reg Reg Reg | MULLW_MayOflo Reg Reg Reg -- dst = 1 if src1 * src2 overflows -- pseudo-instruction; pretty-printed as: -- mullwo. dst, src1, src2 -- mfxer dst -- rlwinm dst, dst, 2, 31,31 | AND Reg Reg RI -- dst, src1, src2 | OR Reg Reg RI -- dst, src1, src2 | XOR Reg Reg RI -- dst, src1, src2 | XORIS Reg Reg Imm -- XOR Immediate Shifted dst, src1, src2 | EXTS Size Reg Reg | NEG Reg Reg | NOT Reg Reg | SLW Reg Reg RI -- shift left word | SRW Reg Reg RI -- shift right word | SRAW Reg Reg RI -- shift right arithmetic word -- Rotate Left Word Immediate then AND with Mask | RLWINM Reg Reg Int Int Int | FADD Size Reg Reg Reg | FSUB Size Reg Reg Reg | FMUL Size Reg Reg Reg | FDIV Size Reg Reg Reg | FNEG Reg Reg -- negate is the same for single and double prec. | FCMP Reg Reg | FCTIWZ Reg Reg -- convert to integer word | FRSP Reg Reg -- reduce to single precision -- (but destination is a FP register) | CRNOR Int Int Int -- condition register nor | MFCR Reg -- move from condition register | MFLR Reg -- move from link register | FETCHPC Reg -- pseudo-instruction: -- bcl to next insn, mflr reg | LWSYNC -- memory barrier -- | Get the registers that are being used by this instruction. -- regUsage doesn't need to do any trickery for jumps and such. -- Just state precisely the regs read and written by that insn. -- The consequences of control flow transfers, as far as register -- allocation goes, are taken care of by the register allocator. -- ppc_regUsageOfInstr :: Instr -> RegUsage ppc_regUsageOfInstr instr = case instr of LD _ reg addr -> usage (regAddr addr, [reg]) LA _ reg addr -> usage (regAddr addr, [reg]) ST _ reg addr -> usage (reg : regAddr addr, []) STU _ reg addr -> usage (reg : regAddr addr, []) LIS reg _ -> usage ([], [reg]) LI reg _ -> usage ([], [reg]) MR reg1 reg2 -> usage ([reg2], [reg1]) CMP _ reg ri -> usage (reg : regRI ri,[]) CMPL _ reg ri -> usage (reg : regRI ri,[]) BCC _ _ -> noUsage BCCFAR _ _ -> noUsage MTCTR reg -> usage ([reg],[]) BCTR _ -> noUsage BL _ params -> usage (params, callClobberedRegs) BCTRL params -> usage (params, callClobberedRegs) ADD reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) ADDC reg1 reg2 reg3-> usage ([reg2,reg3], [reg1]) ADDE reg1 reg2 reg3-> usage ([reg2,reg3], [reg1]) ADDIS reg1 reg2 _ -> usage ([reg2], [reg1]) SUBF reg1 reg2 reg3-> usage ([reg2,reg3], [reg1]) MULLW reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) DIVW reg1 reg2 reg3-> usage ([reg2,reg3], [reg1]) DIVWU reg1 reg2 reg3-> usage ([reg2,reg3], [reg1]) MULLW_MayOflo reg1 reg2 reg3 -> usage ([reg2,reg3], [reg1]) AND reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) OR reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) XOR reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) XORIS reg1 reg2 _ -> usage ([reg2], [reg1]) EXTS _ reg1 reg2 -> usage ([reg2], [reg1]) NEG reg1 reg2 -> usage ([reg2], [reg1]) NOT reg1 reg2 -> usage ([reg2], [reg1]) SLW reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) SRW reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) SRAW reg1 reg2 ri -> usage (reg2 : regRI ri, [reg1]) RLWINM reg1 reg2 _ _ _ -> usage ([reg2], [reg1]) FADD _ r1 r2 r3 -> usage ([r2,r3], [r1]) FSUB _ r1 r2 r3 -> usage ([r2,r3], [r1]) FMUL _ r1 r2 r3 -> usage ([r2,r3], [r1]) FDIV _ r1 r2 r3 -> usage ([r2,r3], [r1]) FNEG r1 r2 -> usage ([r2], [r1]) FCMP r1 r2 -> usage ([r1,r2], []) FCTIWZ r1 r2 -> usage ([r2], [r1]) FRSP r1 r2 -> usage ([r2], [r1]) MFCR reg -> usage ([], [reg]) MFLR reg -> usage ([], [reg]) FETCHPC reg -> usage ([], [reg]) _ -> noUsage where usage (src, dst) = RU (filter interesting src) (filter interesting dst) regAddr (AddrRegReg r1 r2) = [r1, r2] regAddr (AddrRegImm r1 _) = [r1] regRI (RIReg r) = [r] regRI _ = [] interesting :: Reg -> Bool interesting (RegVirtual _) = True interesting (RegReal (RealRegSingle i)) = isFastTrue (freeReg i) interesting (RegReal (RealRegPair{})) = panic "PPC.Instr.interesting: no reg pairs on this arch" -- | Apply a given mapping to all the register references in this -- instruction. ppc_patchRegsOfInstr :: Instr -> (Reg -> Reg) -> Instr ppc_patchRegsOfInstr instr env = case instr of LD sz reg addr -> LD sz (env reg) (fixAddr addr) LA sz reg addr -> LA sz (env reg) (fixAddr addr) ST sz reg addr -> ST sz (env reg) (fixAddr addr) STU sz reg addr -> STU sz (env reg) (fixAddr addr) LIS reg imm -> LIS (env reg) imm LI reg imm -> LI (env reg) imm MR reg1 reg2 -> MR (env reg1) (env reg2) CMP sz reg ri -> CMP sz (env reg) (fixRI ri) CMPL sz reg ri -> CMPL sz (env reg) (fixRI ri) BCC cond lbl -> BCC cond lbl BCCFAR cond lbl -> BCCFAR cond lbl MTCTR reg -> MTCTR (env reg) BCTR targets -> BCTR targets BL imm argRegs -> BL imm argRegs -- argument regs BCTRL argRegs -> BCTRL argRegs -- cannot be remapped ADD reg1 reg2 ri -> ADD (env reg1) (env reg2) (fixRI ri) ADDC reg1 reg2 reg3-> ADDC (env reg1) (env reg2) (env reg3) ADDE reg1 reg2 reg3-> ADDE (env reg1) (env reg2) (env reg3) ADDIS reg1 reg2 imm -> ADDIS (env reg1) (env reg2) imm SUBF reg1 reg2 reg3-> SUBF (env reg1) (env reg2) (env reg3) MULLW reg1 reg2 ri -> MULLW (env reg1) (env reg2) (fixRI ri) DIVW reg1 reg2 reg3-> DIVW (env reg1) (env reg2) (env reg3) DIVWU reg1 reg2 reg3-> DIVWU (env reg1) (env reg2) (env reg3) MULLW_MayOflo reg1 reg2 reg3 -> MULLW_MayOflo (env reg1) (env reg2) (env reg3) AND reg1 reg2 ri -> AND (env reg1) (env reg2) (fixRI ri) OR reg1 reg2 ri -> OR (env reg1) (env reg2) (fixRI ri) XOR reg1 reg2 ri -> XOR (env reg1) (env reg2) (fixRI ri) XORIS reg1 reg2 imm -> XORIS (env reg1) (env reg2) imm EXTS sz reg1 reg2 -> EXTS sz (env reg1) (env reg2) NEG reg1 reg2 -> NEG (env reg1) (env reg2) NOT reg1 reg2 -> NOT (env reg1) (env reg2) SLW reg1 reg2 ri -> SLW (env reg1) (env reg2) (fixRI ri) SRW reg1 reg2 ri -> SRW (env reg1) (env reg2) (fixRI ri) SRAW reg1 reg2 ri -> SRAW (env reg1) (env reg2) (fixRI ri) RLWINM reg1 reg2 sh mb me -> RLWINM (env reg1) (env reg2) sh mb me FADD sz r1 r2 r3 -> FADD sz (env r1) (env r2) (env r3) FSUB sz r1 r2 r3 -> FSUB sz (env r1) (env r2) (env r3) FMUL sz r1 r2 r3 -> FMUL sz (env r1) (env r2) (env r3) FDIV sz r1 r2 r3 -> FDIV sz (env r1) (env r2) (env r3) FNEG r1 r2 -> FNEG (env r1) (env r2) FCMP r1 r2 -> FCMP (env r1) (env r2) FCTIWZ r1 r2 -> FCTIWZ (env r1) (env r2) FRSP r1 r2 -> FRSP (env r1) (env r2) MFCR reg -> MFCR (env reg) MFLR reg -> MFLR (env reg) FETCHPC reg -> FETCHPC (env reg) _ -> instr where fixAddr (AddrRegReg r1 r2) = AddrRegReg (env r1) (env r2) fixAddr (AddrRegImm r1 i) = AddrRegImm (env r1) i fixRI (RIReg r) = RIReg (env r) fixRI other = other -------------------------------------------------------------------------------- -- | Checks whether this instruction is a jump/branch instruction. -- One that can change the flow of control in a way that the -- register allocator needs to worry about. ppc_isJumpishInstr :: Instr -> Bool ppc_isJumpishInstr instr = case instr of BCC{} -> True BCCFAR{} -> True BCTR{} -> True BCTRL{} -> True BL{} -> True JMP{} -> True _ -> False -- | Checks whether this instruction is a jump/branch instruction. -- One that can change the flow of control in a way that the -- register allocator needs to worry about. ppc_jumpDestsOfInstr :: Instr -> [BlockId] ppc_jumpDestsOfInstr insn = case insn of BCC _ id -> [id] BCCFAR _ id -> [id] BCTR targets -> targets _ -> [] -- | Change the destination of this jump instruction. -- Used in the linear allocator when adding fixup blocks for join -- points. ppc_patchJumpInstr :: Instr -> (BlockId -> BlockId) -> Instr ppc_patchJumpInstr insn patchF = case insn of BCC cc id -> BCC cc (patchF id) BCCFAR cc id -> BCCFAR cc (patchF id) BCTR _ -> error "Cannot patch BCTR" _ -> insn -- ----------------------------------------------------------------------------- -- | An instruction to spill a register into a spill slot. ppc_mkSpillInstr :: Reg -- register to spill -> Int -- current stack delta -> Int -- spill slot to use -> Instr ppc_mkSpillInstr reg delta slot = let off = spillSlotToOffset slot in let sz = case targetClassOfReg reg of RcInteger -> II32 RcDouble -> FF64 _ -> panic "PPC.Instr.mkSpillInstr: no match" in ST sz reg (AddrRegImm sp (ImmInt (off-delta))) ppc_mkLoadInstr :: Reg -- register to load -> Int -- current stack delta -> Int -- spill slot to use -> Instr ppc_mkLoadInstr reg delta slot = let off = spillSlotToOffset slot in let sz = case targetClassOfReg reg of RcInteger -> II32 RcDouble -> FF64 _ -> panic "PPC.Instr.mkLoadInstr: no match" in LD sz reg (AddrRegImm sp (ImmInt (off-delta))) spillSlotSize :: Int spillSlotSize = 8 maxSpillSlots :: Int maxSpillSlots = ((rESERVED_C_STACK_BYTES - 64) `div` spillSlotSize) - 1 -- convert a spill slot number to a *byte* offset, with no sign: -- decide on a per arch basis whether you are spilling above or below -- the C stack pointer. spillSlotToOffset :: Int -> Int spillSlotToOffset slot | slot >= 0 && slot < maxSpillSlots = 64 + spillSlotSize * slot | otherwise = pprPanic "spillSlotToOffset:" ( text "invalid spill location: " <> int slot $$ text "maxSpillSlots: " <> int maxSpillSlots) -------------------------------------------------------------------------------- -- | See if this instruction is telling us the current C stack delta ppc_takeDeltaInstr :: Instr -> Maybe Int ppc_takeDeltaInstr instr = case instr of DELTA i -> Just i _ -> Nothing ppc_isMetaInstr :: Instr -> Bool ppc_isMetaInstr instr = case instr of COMMENT{} -> True LDATA{} -> True NEWBLOCK{} -> True DELTA{} -> True _ -> False -- | Copy the value in a register to another one. -- Must work for all register classes. ppc_mkRegRegMoveInstr :: Reg -> Reg -> Instr ppc_mkRegRegMoveInstr src dst = MR dst src -- | Make an unconditional jump instruction. -- For architectures with branch delay slots, its ok to put -- a NOP after the jump. Don't fill the delay slot with an -- instruction that references regs or you'll confuse the -- linear allocator. ppc_mkJumpInstr :: BlockId -> [Instr] ppc_mkJumpInstr id = [BCC ALWAYS id] -- | Take the source and destination from this reg -> reg move instruction -- or Nothing if it's not one ppc_takeRegRegMoveInstr :: Instr -> Maybe (Reg,Reg) ppc_takeRegRegMoveInstr (MR dst src) = Just (src,dst) ppc_takeRegRegMoveInstr _ = Nothing