PPC NCG: Fix and refactor TOC handling.
authorPeter Trommler <ptrommler@acm.org>
Sat, 18 Jun 2016 10:29:12 +0000 (12:29 +0200)
committerBen Gamari <ben@smart-cactus.org>
Sat, 18 Jun 2016 22:27:06 +0000 (00:27 +0200)
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

compiler/nativeGen/PPC/CodeGen.hs

index 86903e4..1b719fc 100644 (file)
@@ -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