summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Trommler <ptrommler@acm.org>2015-10-02 15:48:30 +0200
committerBen Gamari <ben@smart-cactus.org>2015-10-02 15:51:09 +0200
commitb29f20edb1ca7f1763ceb001e2bb2d5f2f11bec3 (patch)
tree88b2fb3943c7608f3f5c1fe9df6a441e9810c6a2
parent57e3742c20fcc55f21634b6a43fbee47bc053775 (diff)
downloadhaskell-b29f20edb1ca7f1763ceb001e2bb2d5f2f11bec3.tar.gz
nativeGen PPC: fix > 16 bit offsets in stack handling
Implement access to spill slots at offsets larger than 16 bits. Also allocation and deallocation of spill slots was restricted to 16 bit offsets. Now 32 bit offsets are supported on all PowerPC platforms. The implementation of 32 bit offsets requires more than one instruction but the native code generator wants one instruction. So we implement pseudo-instructions that are pretty printed into multiple assembly instructions. With pseudo-instructions for spill slot allocation and deallocation we can also implement handling of the back chain pointer according to the ELF ABIs. Test Plan: validate (especially on powerpc (32 bit)) Reviewers: bgamari, austin, erikd Reviewed By: erikd Subscribers: thomie Differential Revision: https://phabricator.haskell.org/D1296 GHC Trac Issues: #7830
-rw-r--r--compiler/nativeGen/PPC/Instr.hs39
-rw-r--r--compiler/nativeGen/PPC/Ppr.hs33
-rw-r--r--compiler/nativeGen/PPC/Regs.hs14
-rw-r--r--includes/CodeGen.Platform.hs18
4 files changed, 86 insertions, 18 deletions
diff --git a/compiler/nativeGen/PPC/Instr.hs b/compiler/nativeGen/PPC/Instr.hs
index 975527817d..b5c26ed906 100644
--- a/compiler/nativeGen/PPC/Instr.hs
+++ b/compiler/nativeGen/PPC/Instr.hs
@@ -74,19 +74,19 @@ instance Instruction Instr where
ppc_mkStackAllocInstr :: Platform -> Int -> Instr
ppc_mkStackAllocInstr platform amount
- = case platformArch platform of
- ArchPPC -> -- SUB II32 (OpImm (ImmInt amount)) (OpReg esp)
- ADD sp sp (RIImm (ImmInt (-amount)))
- ArchPPC_64 _ -> STU II64 sp (AddrRegImm sp (ImmInt (-amount)))
- arch -> panic $ "ppc_mkStackAllocInstr " ++ show arch
+ = ppc_mkStackAllocInstr' platform (-amount)
ppc_mkStackDeallocInstr :: Platform -> Int -> Instr
ppc_mkStackDeallocInstr platform amount
+ = ppc_mkStackAllocInstr' platform amount
+
+ppc_mkStackAllocInstr' :: Platform -> Int -> Instr
+ppc_mkStackAllocInstr' platform amount
= case platformArch platform of
- ArchPPC -> -- ADD II32 (OpImm (ImmInt amount)) (OpReg esp)
- ADD sp sp (RIImm (ImmInt amount))
- ArchPPC_64 _ -> ADD sp sp (RIImm (ImmInt amount))
- arch -> panic $ "ppc_mkStackDeallocInstr " ++ show arch
+ ArchPPC -> UPDATE_SP II32 (ImmInt amount)
+ ArchPPC_64 _ -> UPDATE_SP II64 (ImmInt amount)
+ _ -> panic $ "ppc_mkStackAllocInstr' "
+ ++ show (platformArch platform)
--
-- See note [extra spill slots] in X86/Instr.hs
@@ -186,8 +186,10 @@ data Instr
-- Loads and stores.
| LD Format Reg AddrMode -- Load format, dst, src
+ | LDFAR Format Reg AddrMode -- Load format, dst, src 32 bit offset
| LA Format Reg AddrMode -- Load arithmetic format, dst, src
| ST Format Reg AddrMode -- Store format, src, dst
+ | STFAR Format Reg AddrMode -- Store format, src, dst 32 bit offset
| STU Format Reg AddrMode -- Store with Update format, src, dst
| LIS Reg Imm -- Load Immediate Shifted dst, src
| LI Reg Imm -- Load Immediate dst, src
@@ -277,6 +279,8 @@ data Instr
| NOP -- no operation, PowerPC 64 bit
-- needs this as place holder to
-- reload TOC pointer
+ | UPDATE_SP Format Imm -- expand/shrink spill area on C stack
+ -- pseudo-instruction
-- | Get the registers that are being used by this instruction.
-- regUsage doesn't need to do any trickery for jumps and such.
@@ -288,8 +292,10 @@ ppc_regUsageOfInstr :: Platform -> Instr -> RegUsage
ppc_regUsageOfInstr platform instr
= case instr of
LD _ reg addr -> usage (regAddr addr, [reg])
+ LDFAR _ reg addr -> usage (regAddr addr, [reg])
LA _ reg addr -> usage (regAddr addr, [reg])
ST _ reg addr -> usage (reg : regAddr addr, [])
+ STFAR _ reg addr -> usage (reg : regAddr addr, [])
STU _ reg addr -> usage (reg : regAddr addr, [])
LIS reg _ -> usage ([], [reg])
LI reg _ -> usage ([], [reg])
@@ -349,6 +355,7 @@ ppc_regUsageOfInstr platform instr
MFLR reg -> usage ([], [reg])
FETCHPC reg -> usage ([], [reg])
FETCHTOC reg _ -> usage ([], [reg])
+ UPDATE_SP _ _ -> usage ([], [sp])
_ -> noUsage
where
usage (src, dst) = RU (filter (interesting platform) src)
@@ -373,8 +380,10 @@ ppc_patchRegsOfInstr :: Instr -> (Reg -> Reg) -> Instr
ppc_patchRegsOfInstr instr env
= case instr of
LD fmt reg addr -> LD fmt (env reg) (fixAddr addr)
+ LDFAR fmt reg addr -> LDFAR fmt (env reg) (fixAddr addr)
LA fmt reg addr -> LA fmt (env reg) (fixAddr addr)
ST fmt reg addr -> ST fmt (env reg) (fixAddr addr)
+ STFAR fmt reg addr -> STFAR fmt (env reg) (fixAddr addr)
STU fmt reg addr -> STU fmt (env reg) (fixAddr addr)
LIS reg imm -> LIS (env reg) imm
LI reg imm -> LI (env reg) imm
@@ -505,7 +514,11 @@ ppc_mkSpillInstr dflags reg delta slot
_ -> II64
RcDouble -> FF64
_ -> panic "PPC.Instr.mkSpillInstr: no match"
- in ST fmt reg (AddrRegImm sp (ImmInt (off-delta)))
+ instr = case makeImmediate W32 True (off-delta) of
+ Just _ -> ST
+ Nothing -> STFAR -- pseudo instruction: 32 bit offsets
+
+ in instr fmt reg (AddrRegImm sp (ImmInt (off-delta)))
ppc_mkLoadInstr
@@ -526,7 +539,11 @@ ppc_mkLoadInstr dflags reg delta slot
_ -> II64
RcDouble -> FF64
_ -> panic "PPC.Instr.mkLoadInstr: no match"
- in LD fmt reg (AddrRegImm sp (ImmInt (off-delta)))
+ instr = case makeImmediate W32 True (off-delta) of
+ Just _ -> LD
+ Nothing -> LDFAR -- pseudo instruction: 32 bit offsets
+
+ in instr fmt reg (AddrRegImm sp (ImmInt (off-delta)))
-- | The maximum number of bytes required to spill a register. PPC32
diff --git a/compiler/nativeGen/PPC/Ppr.hs b/compiler/nativeGen/PPC/Ppr.hs
index 6b9150a2d1..9fabc949fd 100644
--- a/compiler/nativeGen/PPC/Ppr.hs
+++ b/compiler/nativeGen/PPC/Ppr.hs
@@ -437,6 +437,15 @@ pprInstr (LD fmt reg addr) = hcat [
ptext (sLit ", "),
pprAddr addr
]
+pprInstr (LDFAR fmt reg (AddrRegImm source off)) =
+ sdocWithPlatform $ \platform -> vcat [
+ pprInstr (ADDIS (tmpReg platform) source (HA off)),
+ pprInstr (LD fmt reg (AddrRegImm (tmpReg platform) (LO off)))
+ ]
+
+pprInstr (LDFAR _ _ _) =
+ panic "PPC.Ppr.pprInstr LDFAR: no match"
+
pprInstr (LA fmt reg addr) = hcat [
char '\t',
ptext (sLit "l"),
@@ -467,6 +476,14 @@ pprInstr (ST fmt reg addr) = hcat [
ptext (sLit ", "),
pprAddr addr
]
+pprInstr (STFAR fmt reg (AddrRegImm source off)) =
+ sdocWithPlatform $ \platform -> vcat [
+ pprInstr (ADDIS (tmpReg platform) source (HA off)),
+ pprInstr (ST fmt reg (AddrRegImm (tmpReg platform) (LO off)))
+ ]
+
+pprInstr (STFAR _ _ _) =
+ panic "PPC.Ppr.pprInstr STFAR: no match"
pprInstr (STU fmt reg addr) = hcat [
char '\t',
ptext (sLit "st"),
@@ -799,6 +816,22 @@ pprInstr LWSYNC = ptext (sLit "\tlwsync")
pprInstr NOP = ptext (sLit "\tnop")
+pprInstr (UPDATE_SP fmt amount@(ImmInt offset))
+ | fits16Bits offset = vcat [
+ pprInstr (LD fmt r0 (AddrRegImm sp (ImmInt 0))),
+ pprInstr (STU fmt r0 (AddrRegImm sp amount))
+ ]
+
+pprInstr (UPDATE_SP fmt amount)
+ = sdocWithPlatform $ \platform ->
+ let tmp = tmpReg platform in
+ vcat [
+ pprInstr (LD fmt r0 (AddrRegImm sp (ImmInt 0))),
+ pprInstr (ADDIS tmp sp (HA amount)),
+ pprInstr (ADD tmp tmp (RIImm (LO amount))),
+ pprInstr (STU fmt r0 (AddrRegReg sp tmp))
+ ]
+
-- pprInstr _ = panic "pprInstr (ppc)"
diff --git a/compiler/nativeGen/PPC/Regs.hs b/compiler/nativeGen/PPC/Regs.hs
index 05efaeb1f4..14bdab734b 100644
--- a/compiler/nativeGen/PPC/Regs.hs
+++ b/compiler/nativeGen/PPC/Regs.hs
@@ -37,7 +37,8 @@ module PPC.Regs (
fits16Bits,
makeImmediate,
fReg,
- sp, toc, r3, r4, r11, r12, r27, r28, r30,
+ r0, sp, toc, r3, r4, r11, r12, r27, r28, r30,
+ tmpReg,
f1, f20, f21,
allocatableRegs
@@ -304,7 +305,8 @@ point registers.
fReg :: Int -> RegNo
fReg x = (32 + x)
-sp, toc, r3, r4, r11, r12, r27, r28, r30, f1, f20, f21 :: Reg
+r0, sp, toc, r3, r4, r11, r12, r27, r28, r30, f1, f20, f21 :: Reg
+r0 = regSingle 0
sp = regSingle 1
toc = regSingle 2
r3 = regSingle 3
@@ -325,3 +327,11 @@ allocatableRegs :: Platform -> [RealReg]
allocatableRegs platform
= let isFree i = freeReg platform i
in map RealRegSingle $ filter isFree allMachRegNos
+
+-- temporary register for compiler use
+tmpReg :: Platform -> Reg
+tmpReg platform =
+ case platformArch platform of
+ ArchPPC -> regSingle 13
+ ArchPPC_64 _ -> regSingle 30
+ _ -> panic "PPC.Regs.tmpReg: unknowm arch"
diff --git a/includes/CodeGen.Platform.hs b/includes/CodeGen.Platform.hs
index b93f0074fd..b41ad54349 100644
--- a/includes/CodeGen.Platform.hs
+++ b/includes/CodeGen.Platform.hs
@@ -874,17 +874,25 @@ freeRegBase _ = True
#elif MACHREGS_powerpc
-freeReg 0 = False -- Hack: r0 can't be used in all insns,
- -- but it's actually free
+freeReg 0 = False -- Used by code setting the back chain pointer
+ -- in stack reallocations on Linux
+ -- r0 is not usable in all insns so also reserved
+ -- on Darwin.
freeReg 1 = False -- The Stack Pointer
# if !MACHREGS_darwin
-- most non-darwin powerpc OSes use r2 as a TOC pointer or something like that
freeReg 2 = False
--- TODO: make this conditonal for ppc64 ELF
-freeReg 13 = False -- reserved for system thread ID
--- TODO: do not reserve r30 in ppc64 ELF
+freeReg 13 = False -- reserved for system thread ID on 64 bit
-- at least linux in -fPIC relies on r30 in PLT stubs
freeReg 30 = False
+{- TODO: reserve r13 on 64 bit systems only and r30 on 32 bit respectively.
+ For now we use r30 on 64 bit and r13 on 32 bit as a temporary register
+ in stack handling code. See compiler/nativeGen/PPC/Ppr.hs.
+
+ Later we might want to reserve r13 and r30 only where it is required.
+ Then use r12 as temporary register, which is also what the C ABI does.
+-}
+
# endif
# ifdef REG_Base
freeReg REG_Base = False