[lld] bae7cf6 - [ELF][PPC64] Synthesize _savegpr[01]_{14..31} and _restgpr[01]_{14..31}

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue May 26 09:35:57 PDT 2020


Author: Fangrui Song
Date: 2020-05-26T09:35:41-07:00
New Revision: bae7cf674621b5892a036fabe77692a59e2b115b

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

LOG: [ELF][PPC64] Synthesize _savegpr[01]_{14..31} and _restgpr[01]_{14..31}

In the 64-bit ELF V2 API Specification: Power Architecture, 2.3.3.1. GPR
Save and Restore Functions defines some special functions which may be
referenced by GCC produced assembly (LLVM does not reference them).

With GCC -Os, when the number of call-saved registers exceeds a certain
threshold, GCC generates `_savegpr0_* _restgpr0_*` calls and expects the
linker to define them. See
https://sourceware.org/pipermail/binutils/2002-February/017444.html and
https://sourceware.org/pipermail/binutils/2004-August/036765.html . This
is weird because libgcc.a would be the natural place. However, the linker
generation approach has the advantage that the linker can generate
multiple copies to avoid long branch thunks. We don't consider the
advantage significant enough to complicate our trunk implementation, so
we take a simple approach.

* Check whether `_savegpr0_{14..31}` are used
* If yes, define needed symbols and add an InputSection with the code sequence.

`_savegpr1_*` `_restgpr0_*` and `_restgpr1_*` are similar.

Reviewed By: sfertile

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

Added: 
    lld/test/ELF/ppc64-restgpr0.s
    lld/test/ELF/ppc64-restgpr1.s
    lld/test/ELF/ppc64-savegpr0.s
    lld/test/ELF/ppc64-savegpr1.s
    lld/test/ELF/ppc64-saveres.s

Modified: 
    lld/ELF/Arch/PPC64.cpp
    lld/ELF/Target.h
    lld/ELF/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index b80f96f28b46..a182c77209ae 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -6,11 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
 #include "Thunks.h"
 #include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -104,6 +106,84 @@ bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
   return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
 }
 
+static bool addOptional(StringRef name, uint64_t value,
+                        std::vector<Defined *> &defined) {
+  Symbol *sym = symtab->find(name);
+  if (!sym || sym->isDefined())
+    return false;
+  sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL,
+                       STV_HIDDEN, STT_FUNC, value,
+                       /*size=*/0, /*section=*/nullptr});
+  defined.push_back(cast<Defined>(sym));
+  return true;
+}
+
+// If from is 14, write ${prefix}14: firstInsn; ${prefix}15:
+// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail
+// The labels are defined only if they exist in the symbol table.
+static void writeSequence(MutableArrayRef<uint32_t> buf, const char *prefix,
+                          int from, uint32_t firstInsn,
+                          ArrayRef<uint32_t> tail) {
+  std::vector<Defined *> defined;
+  char name[16];
+  int first;
+  uint32_t *ptr = buf.data();
+  for (int r = from; r < 32; ++r) {
+    format("%s%d", prefix, r).snprint(name, sizeof(name));
+    if (addOptional(name, 4 * (r - from), defined) && defined.size() == 1)
+      first = r - from;
+    write32(ptr++, firstInsn + 0x200008 * (r - from));
+  }
+  for (uint32_t insn : tail)
+    write32(ptr++, insn);
+  assert(ptr == &*buf.end());
+
+  if (defined.empty())
+    return;
+  // The full section content has the extent of [begin, end). We drop unused
+  // instructions and write [first,end).
+  auto *sec = make<InputSection>(
+      nullptr, SHF_ALLOC, SHT_PROGBITS, 4,
+      makeArrayRef(reinterpret_cast<uint8_t *>(buf.data() + first),
+                   4 * (buf.size() - first)),
+      ".text");
+  inputSections.push_back(sec);
+  for (Defined *sym : defined) {
+    sym->section = sec;
+    sym->value -= 4 * first;
+  }
+}
+
+// Implements some save and restore functions as described by ELF V2 ABI to be
+// compatible with GCC. With GCC -Os, when the number of call-saved registers
+// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and
+// expects the linker to define them. See
+// https://sourceware.org/pipermail/binutils/2002-February/017444.html and
+// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is
+// weird because libgcc.a would be the natural place. The linker generation
+// approach has the advantage that the linker can generate multiple copies to
+// avoid long branch thunks. However, we don't consider the advantage
+// significant enough to complicate our trunk implementation, so we take the
+// simple approach and synthesize .text sections providing the implementation.
+void elf::addPPC64SaveRestore() {
+  static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19];
+  constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6;
+
+  // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ...
+  // Tail: ld 0, 16(1); mtlr 0; blr
+  writeSequence(restgpr0, "_restgpr0_", 14, 0xe9c1ff70,
+                {0xe8010010, mtlr_0, blr});
+  // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ...
+  // Tail: blr
+  writeSequence(restgpr1, "_restgpr1_", 14, 0xe9ccff70, {blr});
+  // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ...
+  // Tail: std 0, 16(1); blr
+  writeSequence(savegpr0, "_savegpr0_", 14, 0xf9c1ff70, {0xf8010010, blr});
+  // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ...
+  // Tail: blr
+  writeSequence(savegpr1, "_savegpr1_", 14, 0xf9ccff70, {blr});
+}
+
 // Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
 template <typename ELFT>
 static std::pair<Defined *, int64_t>

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index a308a41ff4b9..47905ae64a47 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -213,6 +213,7 @@ unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
 // the .toc section.
 bool isPPC64SmallCodeModelTocReloc(RelType type);
 
+void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
 

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index f782cd3cbc45..9a6be7931a28 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -264,6 +264,8 @@ void elf::addReservedSymbols() {
     // glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
     // support Small Data Area, define it arbitrarily as 0.
     addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN);
+  } else if (config->emachine == EM_PPC64) {
+    addPPC64SaveRestore();
   }
 
   // The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which

diff  --git a/lld/test/ELF/ppc64-restgpr0.s b/lld/test/ELF/ppc64-restgpr0.s
new file mode 100644
index 000000000000..3627272dab55
--- /dev/null
+++ b/lld/test/ELF/ppc64-restgpr0.s
@@ -0,0 +1,38 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _restgpr0_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_restgpr0_14>:
+# R14-NEXT:    ld 14, -144(1)
+# R14-NEXT:    ld 15, -136(1)
+# R14-EMPTY:
+# R14-NEXT:  <_restgpr0_16>:
+# R14-NEXT:    ld 16, -128(1)
+# R14:         ld 31, -8(1)
+# R14-NEXT:    ld 0, 16(1)
+# R14-NEXT:    mtlr 0
+# R14-NEXT:    blr
+
+## Don't synthesize _restgpr0_{14..30} because they are unused.
+# RUN: echo 'bl _restgpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_restgpr0_31>:
+# R31-NEXT:    ld 31, -8(1)
+# R31-NEXT:    ld 0, 16(1)
+# R31-NEXT:    mtlr 0
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _restgpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _restgpr0_14
+  bl _restgpr0_16

diff  --git a/lld/test/ELF/ppc64-restgpr1.s b/lld/test/ELF/ppc64-restgpr1.s
new file mode 100644
index 000000000000..e4b97daf06d7
--- /dev/null
+++ b/lld/test/ELF/ppc64-restgpr1.s
@@ -0,0 +1,34 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _restgpr1_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14:       <_restgpr1_14>:
+# R14-NEXT:    ld 14, -144(12)
+# R14-NEXT:    ld 15, -136(12)
+# R14-EMPTY:
+# R14-NEXT:  <_restgpr1_16>:
+# R14-NEXT:    ld 16, -128(12)
+# R14:         ld 31, -8(12)
+# R14-NEXT:    blr
+
+## Don't synthesize _restgpr1_{14..30} because they are unused.
+# RUN: echo 'bl _restgpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_restgpr1_31>:
+# R31-NEXT:    ld 31, -8(12)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _restgpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _restgpr1_14
+  bl _restgpr1_16

diff  --git a/lld/test/ELF/ppc64-savegpr0.s b/lld/test/ELF/ppc64-savegpr0.s
new file mode 100644
index 000000000000..1e85340b99df
--- /dev/null
+++ b/lld/test/ELF/ppc64-savegpr0.s
@@ -0,0 +1,36 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _savegpr0_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_savegpr0_14>:
+# R14-NEXT:    std 14, -144(1)
+# R14-NEXT:    std 15, -136(1)
+# R14-EMPTY:
+# R14-NEXT:  <_savegpr0_16>:
+# R14-NEXT:    std 16, -128(1)
+# R14:         std 31, -8(1)
+# R14-NEXT:    std 0, 16(1)
+# R14-NEXT:    blr
+
+## Don't synthesize _savegpr0_{14..30} because they are unused.
+# RUN: echo 'bl _savegpr0_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_savegpr0_31>:
+# R31-NEXT:    std 31, -8(1)
+# R31-NEXT:    std 0, 16(1)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _savegpr0_32' | llvm-mc -filetype=obj -triple=ppc64 - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _savegpr0_14
+  bl _savegpr0_16

diff  --git a/lld/test/ELF/ppc64-savegpr1.s b/lld/test/ELF/ppc64-savegpr1.s
new file mode 100644
index 000000000000..abb878285f82
--- /dev/null
+++ b/lld/test/ELF/ppc64-savegpr1.s
@@ -0,0 +1,34 @@
+# REQUIRES: ppc
+## Test code sequences of synthesized _savegpr1_{14..31}
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t14.o
+# RUN: ld.lld %t14.o -o %t14
+# RUN: llvm-objdump -d %t14 | FileCheck --check-prefix=R14 %s
+
+# R14-LABEL: <_savegpr1_14>:
+# R14-NEXT:    std 14, -144(12)
+# R14-NEXT:    std 15, -136(12)
+# R14-EMPTY:
+# R14-NEXT:  <_savegpr1_16>:
+# R14-NEXT:    std 16, -128(12)
+# R14:         std 31, -8(12)
+# R14-NEXT:    blr
+
+## Don't synthesize _savegpr1_{14..30} because they are unused.
+# RUN: echo 'bl _savegpr1_31' | llvm-mc -filetype=obj -triple=ppc64 - -o %t31.o
+# RUN: ld.lld %t31.o -o %t31
+# RUN: llvm-objdump -d %t31 | FileCheck --check-prefix=R31 %s
+
+# R31-LABEL: Disassembly of section .text:
+# R31-EMPTY:
+# R31-NEXT:  <_savegpr1_31>:
+# R31-NEXT:    std 31, -8(12)
+# R31-NEXT:    blr
+
+# RUN: echo 'bl _savegpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o
+# RUN: not ld.lld %t32.o -o /dev/null
+
+.globl _start
+_start:
+  bl _savegpr1_14
+  bl _savegpr1_16

diff  --git a/lld/test/ELF/ppc64-saveres.s b/lld/test/ELF/ppc64-saveres.s
new file mode 100644
index 000000000000..70ef71779952
--- /dev/null
+++ b/lld/test/ELF/ppc64-saveres.s
@@ -0,0 +1,31 @@
+# REQUIRES: ppc
+## Test that some save and restore functions can be synthesized.
+## The code sequences are tested by ppc64-restgpr*.s and ppc64-savegpr*.s
+
+# RUN: llvm-mc -filetype=obj -triple=ppc64le %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -s %t.so | FileCheck --check-prefix=NM %s
+# RUN: llvm-objdump -d %t.so | FileCheck %s
+
+## The synthesized symbols are not exported.
+# NM:      FUNC LOCAL HIDDEN {{.*}} _restgpr0_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _restgpr1_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr0_30
+# NM-NEXT: FUNC LOCAL HIDDEN {{.*}} _savegpr1_30
+
+# CHECK: 00000000000[[#%x,RESTGPR0:]] <_restgpr0_30>:
+# CHECK: 00000000000[[#%x,RESTGPR1:]] <_restgpr1_30>:
+# CHECK: 00000000000[[#%x,SAVEGPR0:]] <_savegpr0_30>:
+# CHECK: 00000000000[[#%x,SAVEGPR1:]] <_savegpr1_30>:
+# CHECK-LABEL: <_start>:
+# CHECK-NEXT:    bl 0x[[#RESTGPR0]]
+# CHECK-NEXT:    bl 0x[[#RESTGPR1]]
+# CHECK-NEXT:    bl 0x[[#SAVEGPR0]]
+# CHECK-NEXT:    bl 0x[[#SAVEGPR1]]
+
+.globl _start
+_start:
+  bl _restgpr0_30
+  bl _restgpr1_30
+  bl _savegpr0_30
+  bl _savegpr1_30


        


More information about the llvm-commits mailing list