Commit f4b0488d authored by Peter Trommler's avatar Peter Trommler 🥁 Committed by Ben Gamari
Browse files

PPC NCG: Fix and refactor TOC handling.

In a call to a fixed function the TOC does not need to be saved.
The linker handles TOC saving.

Refactor TOC handling by folding the two functions toc_before and
toc_after into the code generating the call sequence. This saves
repeating the case distinction in those two functions.

Test Plan: validate on PowerPC 32-bit Linux and AIX

Reviewers: hvr, simonmar, austin, erikd, bgamari

Reviewed By: bgamari

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D2328
parent 2897be77
......@@ -1150,7 +1150,13 @@ genCCall'
PowerPC 64 Linux uses the System V Release 4 Calling Convention for
64-bit PowerPC. It is specified in
"64-bit PowerPC ELF Application Binary Interface Supplement 1.9".
"64-bit PowerPC ELF Application Binary Interface Supplement 1.9"
(PPC64 ELF v1.9).
PowerPC 64 Linux in little endian mode uses the "Power Architecture 64-Bit
ELF V2 ABI Specification -- OpenPOWER ABI for Linux Supplement"
(PPC64 ELF v2).
AIX follows the "PowerOpen ABI: Application Binary Interface Big-Endian
32-Bit Hardware Implementation"
According to all conventions, the parameter area should be part of the
caller's stack frame, allocated in the caller's prologue code (large enough
......@@ -1191,41 +1197,46 @@ genCCall' dflags gcp target dest_regs args
PrimTarget mop -> outOfLineMachOp mop
let codeBefore = move_sp_down finalStack `appOL` passArgumentsCode
`appOL` toc_before
codeAfter = toc_after labelOrExpr `appOL` move_sp_up finalStack
`appOL` moveResult reduceToFF32
codeAfter = move_sp_up finalStack `appOL` moveResult reduceToFF32
case labelOrExpr of
Left lbl -> do -- the linker does all the work for us
return ( codeBefore
`snocOL` BL lbl usedRegs
`appOL` maybeNOP -- some ABI require a NOP after BL
`appOL` codeAfter)
Right dyn -> do -- implement call through function pointer
(dynReg, dynCode) <- getSomeReg dyn
case gcp of
GCPLinux64ELF 1 -> return ( dynCode
`appOL` codeBefore
`snocOL` ST spFormat toc (AddrRegImm sp (ImmInt 40))
`snocOL` LD II64 r11 (AddrRegImm dynReg (ImmInt 0))
`snocOL` LD II64 toc (AddrRegImm dynReg (ImmInt 8))
`snocOL` MTCTR r11
`snocOL` LD II64 r11 (AddrRegImm dynReg (ImmInt 16))
`snocOL` BCTRL usedRegs
`snocOL` LD spFormat toc (AddrRegImm sp (ImmInt 40))
`appOL` codeAfter)
GCPLinux64ELF 2 -> return ( dynCode
`appOL` codeBefore
`snocOL` ST spFormat toc (AddrRegImm sp (ImmInt 24))
`snocOL` MR r12 dynReg
`snocOL` MTCTR r12
`snocOL` BCTRL usedRegs
`snocOL` LD spFormat toc (AddrRegImm sp (ImmInt 24))
`appOL` codeAfter)
GCPAIX -> return ( dynCode
-- AIX/XCOFF follows the PowerOPEN ABI
-- which is quite similiar to LinuxPPC64/ELFv1
`appOL` codeBefore
`snocOL` ST spFormat toc (AddrRegImm sp (ImmInt 20))
`snocOL` LD II32 r11 (AddrRegImm dynReg (ImmInt 0))
`snocOL` LD II32 toc (AddrRegImm dynReg (ImmInt 4))
`snocOL` MTCTR r11
`snocOL` LD II32 r11 (AddrRegImm dynReg (ImmInt 8))
`snocOL` BCTRL usedRegs
`snocOL` LD spFormat toc (AddrRegImm sp (ImmInt 20))
`appOL` codeAfter)
_ -> return ( dynCode
`snocOL` MTCTR dynReg
......@@ -1281,30 +1292,6 @@ genCCall' dflags gcp target dest_regs args
DELTA (-delta)]
| otherwise = nilOL
where delta = stackDelta finalStack
toc_before = case gcp of
GCPLinux64ELF 1 -> unitOL $ ST spFormat toc (AddrRegImm sp (ImmInt 40))
GCPLinux64ELF 2 -> unitOL $ ST spFormat toc (AddrRegImm sp (ImmInt 24))
GCPAIX -> unitOL $ ST spFormat toc (AddrRegImm sp (ImmInt 20))
_ -> nilOL
toc_after labelOrExpr = case gcp of
GCPLinux64ELF 1 -> case labelOrExpr of
Left _ -> toOL [ NOP ]
Right _ -> toOL [ LD spFormat toc
(AddrRegImm sp
(ImmInt 40))
]
GCPLinux64ELF 2 -> case labelOrExpr of
Left _ -> toOL [ NOP ]
Right _ -> toOL [ LD spFormat toc
(AddrRegImm sp
(ImmInt 24))
]
GCPAIX -> case labelOrExpr of
Left _ -> unitOL NOP
Right _ -> unitOL (LD spFormat toc
(AddrRegImm sp
(ImmInt 20)))
_ -> nilOL
move_sp_up finalStack
| delta > 64 = -- TODO: fix-up stack back-chain
toOL [ADD sp sp (RIImm (ImmInt delta)),
......@@ -1312,6 +1299,19 @@ genCCall' dflags gcp target dest_regs args
| otherwise = nilOL
where delta = stackDelta finalStack
-- A NOP instruction is required after a call (bl instruction)
-- on AIX and 64-Bit Linux.
-- If the call is to a function with a different TOC (r2) the
-- link editor replaces the NOP instruction with a load of the TOC
-- from the stack to restore the TOC.
maybeNOP = case gcp of
-- See Section 3.9.4 of OpenPower ABI
GCPAIX -> unitOL NOP
-- See Section 3.5.11 of PPC64 ELF v1.9
GCPLinux64ELF 1 -> unitOL NOP
-- See Section 2.3.6 of PPC64 ELF v2
GCPLinux64ELF 2 -> unitOL NOP
_ -> nilOL
passArguments [] _ _ stackOffset accumCode accumUsed = return (stackOffset, accumCode, accumUsed)
passArguments ((arg,arg_ty):args) gprs fprs stackOffset
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment