[lld] 91cce1a - [PowerPC] Implement R_PPC64_REL24_NOTOC local calls, callee requires a TOC

Victor Huang via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 10:47:58 PDT 2020


Author: Victor Huang
Date: 2020-07-20T17:46:49Z
New Revision: 91cce1a2bc346f9fec5284f777b404dafad5ff3c

URL: https://github.com/llvm/llvm-project/commit/91cce1a2bc346f9fec5284f777b404dafad5ff3c
DIFF: https://github.com/llvm/llvm-project/commit/91cce1a2bc346f9fec5284f777b404dafad5ff3c.diff

LOG: [PowerPC] Implement R_PPC64_REL24_NOTOC local calls, callee requires a TOC

The PC Relative code now allows for calls that are marked with the relocation
R_PPC64_REL24_NOTOC. This indicates that the caller does not have a valid TOC
pointer in R2 and does not require R2 to be restored after the call.

This patch is added to support local calls to callees that require a TOC

Reviewed By: sfertile, MaskRay, nemanjai, stefanp

Differential Revision: https://reviews.llvm.org/D83504

Added: 
    lld/test/ELF/ppc64-pcrel-call-to-toc.s

Modified: 
    lld/ELF/Arch/PPC64.cpp
    lld/ELF/Target.h
    lld/ELF/Thunks.cpp
    llvm/include/llvm/Object/ELF.h

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 71c568088fb9..da0b510d96a1 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -106,6 +106,11 @@ bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
   return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
 }
 
+void elf::writePrefixedInstruction(uint8_t *loc, uint64_t insn) {
+  insn = config->isLE ? insn << 32 | insn >> 32 : insn;
+  write64(loc, insn);
+}
+
 static bool addOptional(StringRef name, uint64_t value,
                         std::vector<Defined *> &defined) {
   Symbol *sym = symtab->find(name);
@@ -376,15 +381,6 @@ static uint32_t readFromHalf16(const uint8_t *loc) {
   return read32(config->isLE ? loc : loc - 2);
 }
 
-// The prefixed instruction is always a 4 byte prefix followed by a 4 byte
-// instruction. Therefore, the prefix is always in lower memory than the
-// instruction (regardless of endianness).
-// As a result, we need to shift the pieces around on little endian machines.
-static void writePrefixedInstruction(uint8_t *loc, uint64_t insn) {
-  insn = config->isLE ? insn << 32 | insn >> 32 : insn;
-  write64(loc, insn);
-}
-
 static uint64_t readPrefixedInstruction(const uint8_t *loc) {
   uint64_t fullInstr = read64(loc);
   return config->isLE ? (fullInstr << 32 | fullInstr >> 32) : fullInstr;
@@ -1048,17 +1044,15 @@ bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
   if (s.isInPlt())
     return true;
 
-  // FIXME: Remove the fatal error once the call protocol is implemented.
-  if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
-    fatal("unimplemented feature: local function call with the reltype"
-          " R_PPC64_REL24_NOTOC and the callee needs toc-pointer setup");
-
   // This check looks at the st_other bits of the callee with relocation
   // R_PPC64_REL14 or R_PPC64_REL24. If the value is 1, then the callee
   // clobbers the TOC and we need an R2 save stub.
   if (type != R_PPC64_REL24_NOTOC && (s.stOther >> 5) == 1)
     return true;
 
+  if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
+    return true;
+
   // If a symbol is a weak undefined and we are compiling an executable
   // it doesn't need a range-extending thunk since it can't be called.
   if (s.isUndefWeak() && !config->shared)

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 47905ae64a47..e53ac4d06627 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -213,6 +213,11 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
 // the .toc section.
 bool isPPC64SmallCodeModelTocReloc(RelType type);
 
+// Write a prefixed instruction, which is a 4-byte prefix followed by a 4-byte
+// instruction (regardless of endianness). Therefore, the prefix is always in
+// lower memory than the instruction.
+void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
+
 void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);

diff  --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index ea74d343ebb2..091a291b26cd 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -293,6 +293,18 @@ class PPC64R2SaveStub final : public Thunk {
   void addSymbols(ThunkSection &isec) override;
 };
 
+// PPC64 R12 Setup Stub
+// When a caller that does not maintain a toc-pointer performs a local call to
+// a callee which requires a toc-pointer then we need this stub to place the
+// callee's global entry point into r12 without a save of R2.
+class PPC64R12SetupStub final : public Thunk {
+public:
+  PPC64R12SetupStub(Symbol &dest) : Thunk(dest, 0) {}
+  uint32_t size() override { return 16; }
+  void writeTo(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
 // A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
 // alignment. This gives a possible 26 bits of 'reach'. If the call offset is
 // larger then that we need to emit a long-branch thunk. The target address
@@ -851,6 +863,23 @@ void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
   s->needsTocRestore = true;
 }
 
+void PPC64R12SetupStub::writeTo(uint8_t *buf) {
+  int64_t offset = destination.getVA() - getThunkTargetSym()->getVA();
+  if (!isInt<34>(offset))
+    fatal("offset must fit in 34 bits to encode in the instruction");
+  uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
+                   (offset & 0xffff);
+
+  writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func at pcrel, 1
+  write32(buf + 8, MTCTR_R12);              // mtctr r12
+  write32(buf + 12, BCTR);                  // bctr
+}
+
+void PPC64R12SetupStub::addSymbols(ThunkSection &isec) {
+  addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0,
+            isec);
+}
+
 void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
   int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
                    getPPC64TocBase();
@@ -974,7 +1003,8 @@ static Thunk *addThunkPPC32(const InputSection &isec, const Relocation &rel,
 }
 
 static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
-  assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) &&
+  assert((type == R_PPC64_REL14 || type == R_PPC64_REL24 ||
+          type == R_PPC64_REL24_NOTOC) &&
          "unexpected relocation type for thunk");
   if (s.isInPlt())
     return make<PPC64PltCallStub>(s);
@@ -984,6 +1014,9 @@ static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
   if ((s.stOther >> 5) == 1)
     return make<PPC64R2SaveStub>(s);
 
+  if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
+    return make<PPC64R12SetupStub>(s);
+
   if (config->picThunk)
     return make<PPC64PILongBranchThunk>(s, a);
 

diff  --git a/lld/test/ELF/ppc64-pcrel-call-to-toc.s b/lld/test/ELF/ppc64-pcrel-call-to-toc.s
new file mode 100644
index 000000000000..d85b559a9cf3
--- /dev/null
+++ b/lld/test/ELF/ppc64-pcrel-call-to-toc.s
@@ -0,0 +1,67 @@
+# REQUIRES: ppc
+# RUN: echo 'SECTIONS { \
+# RUN:   .text_func   0x10010000 : { *(.text_func) } \
+# RUN:   .text_callee 0x10020000 : { *(.text_callee) } \
+# RUN:   .text_caller 0x10030000 : { *(.text_caller) } \
+# RUN:   }' > %t.script
+
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
+# RUN: ld.lld -T %t.script %t.o -o %t
+# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL
+# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o
+# RUN: ld.lld -T %t.script %t.o -o %t
+# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL
+# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t | FileCheck %s
+
+## When a function without TOC accesses a function using TOC, an r12 setup stub
+## is inserted
+
+# SYMBOL:      1: 0000000010020000 0 NOTYPE LOCAL DEFAULT [<other: 0x60>] 2 callee
+# SYMBOL-NEXT: 2: 0000000010030000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 3 caller
+# SYMBOL-NEXT: 3: 0000000010010000 0 NOTYPE LOCAL DEFAULT 1 func
+# SYMBOL:      6: 000000001003000c 16 FUNC LOCAL DEFAULT 3 __gep_setup_callee
+
+# CHECK-LABEL: <func>:
+# CHECK-NEXT:  blr
+
+# CHECK-LABEL: <callee>:
+# CHECK:       bl 0x10010000
+# CHECK-NEXT:  addis 4, 2, -1
+# CHECK-NEXT:  lwz 4, 32744(4)
+# CHECK-NEXT:  blr
+
+# CHECK-LABEL: <caller>:
+# CHECK-NEXT:  bl 0x1003000c
+# CHECK-NEXT:  blr
+
+# CHECK-LABEL: <__gep_setup_callee>:
+# CHECK-NEXT:  paddi 12, 0, -65548, 1
+# CHECK-NEXT:  mtctr 12
+# CHECK-NEXT:  bctr
+
+.section .text_func, "ax", %progbits
+func:
+  blr
+
+.section .text_callee, "ax", %progbits
+callee:
+.Lfunc_gep1:
+  addis 2, 12, .TOC.-.Lfunc_gep1 at ha
+  addi 2, 2, .TOC.-.Lfunc_gep1 at l
+.Lfunc_lep1:
+  .localentry callee, .Lfunc_lep1-.Lfunc_gep1
+  bl func
+  addis 4, 2, global at toc@ha
+  lwz 4, global at toc@l(4)
+  blr
+
+.section .text_caller, "ax", %progbits
+caller:
+  .localentry caller, 1
+  bl callee at notoc
+  blr
+global:
+  .long	0
+  .size	global, 4

diff  --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index f4979ac06503..3dab0471a239 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -48,6 +48,12 @@ static inline Error createError(const Twine &Err) {
   return make_error<StringError>(Err, object_error::parse_failed);
 }
 
+enum PPCInstrMasks : uint64_t {
+  PADDI_R12_NO_DISP = 0x0610000039800000,
+  MTCTR_R12 = 0x7D8903A6,
+  BCTR = 0x4E800420,
+};
+
 template <class ELFT> class ELFFile;
 
 template <class ELFT>


        


More information about the llvm-commits mailing list