diff --git a/lld/ELF/Arch/PPC64SaveRest.cpp b/lld/ELF/Arch/PPC64SaveRest.cpp new file mode 100644 --- /dev/null +++ b/lld/ELF/Arch/PPC64SaveRest.cpp @@ -0,0 +1,246 @@ +//===- PPC64SaveRest.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file 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. 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 the +// simple approach: +// +// if such magic symbols are referenced, assemble the predefined .s, and add the +// object files as LazyObjFile. +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "InputFiles.h" +#include "lld/Common/Memory.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; +using namespace lld; +using namespace lld::elf; + +// clang-format off +/* Generated by: +awk 'BEGIN{ +print "static constexpr char savegpr0[] = R\"(" + print ".section .text,\"ax\",@progbits,unique,0" + printf ".globl "; for(i=14;i<32;i++) { printf "_savegpr0_" i (i==31?"\n":",") } + printf ".hidden "; for(i=14;i<32;i++) { printf "_savegpr0_" i (i==31?"\n":",") } + for(i=14;i<32;i++) { print "_savegpr0_" i ": " s "std " i "," (i*8-256) "(1)" } + print "std 0,16(1)\nblr" + + print "\n.section .text,\"ax\",@progbits,unique,1" + printf ".globl "; for(i=14;i<32;i++) { printf "_restgpr0_" i (i==31?"\n":",") } + printf ".hidden "; for(i=14;i<32;i++) { printf "_restgpr0_" i (i==31?"\n":",") } + for(i=14;i<29;i++) { print "_restgpr0_" i ": " s "ld " i "," (i*8-256) "(1)" } + print "_restgpr0_29: ld 0,16(1)\nld 29,-24(1)\nmtlr 0\nld 30,-16(1)\nld 31,-8(1)\nblr" + print "_restgpr0_30: ld 30,-16(1)" + print "_restgpr0_31: ld 0,16(1)\nld 31,-8(1)\nmtlr 0\nblr" +print ")\";\n" + +print "static constexpr char savegpr1[] = R\"(" + print ".section .text,\"ax\",@progbits,unique,0" + printf ".globl "; for(i=14;i<32;i++) { printf "_savegpr1_" i (i==31?"\n":",") } + printf ".hidden "; for(i=14;i<32;i++) { printf "_savegpr1_" i (i==31?"\n":",") } + for(i=14;i<32;i++) { print "_savegpr1_" i ": " s "std " i "," (i*8-256) "(12)" } + print "blr" + + print "\n.section .text,\"ax\",@progbits,unique,1" + printf ".globl "; for(i=14;i<32;i++) { printf "_restgpr1_" i (i==31?"\n":",") } + printf ".hidden "; for(i=14;i<32;i++) { printf "_restgpr1_" i (i==31?"\n":",") } + for(i=14;i<32;i++) { print "_restgpr1_" i ": " s "ld " i "," (i*8-256) "(12)" } + print "blr" +print ")\";\n" +}' /dev/null +*/ + +// We could produce function sections and add artificial R_PPC64_NONE +// relocations to allow --gc-sections to collect unneeded functions. However, +// that will break --shuffle-sections. + +static constexpr char savegpr0[] = R"( +.section .text,"ax",@progbits,unique,0 +.globl _savegpr0_14,_savegpr0_15,_savegpr0_16,_savegpr0_17,_savegpr0_18,_savegpr0_19,_savegpr0_20,_savegpr0_21,_savegpr0_22,_savegpr0_23,_savegpr0_24,_savegpr0_25,_savegpr0_26,_savegpr0_27,_savegpr0_28,_savegpr0_29,_savegpr0_30,_savegpr0_31 +.hidden _savegpr0_14,_savegpr0_15,_savegpr0_16,_savegpr0_17,_savegpr0_18,_savegpr0_19,_savegpr0_20,_savegpr0_21,_savegpr0_22,_savegpr0_23,_savegpr0_24,_savegpr0_25,_savegpr0_26,_savegpr0_27,_savegpr0_28,_savegpr0_29,_savegpr0_30,_savegpr0_31 +_savegpr0_14: std 14,-144(1) +_savegpr0_15: std 15,-136(1) +_savegpr0_16: std 16,-128(1) +_savegpr0_17: std 17,-120(1) +_savegpr0_18: std 18,-112(1) +_savegpr0_19: std 19,-104(1) +_savegpr0_20: std 20,-96(1) +_savegpr0_21: std 21,-88(1) +_savegpr0_22: std 22,-80(1) +_savegpr0_23: std 23,-72(1) +_savegpr0_24: std 24,-64(1) +_savegpr0_25: std 25,-56(1) +_savegpr0_26: std 26,-48(1) +_savegpr0_27: std 27,-40(1) +_savegpr0_28: std 28,-32(1) +_savegpr0_29: std 29,-24(1) +_savegpr0_30: std 30,-16(1) +_savegpr0_31: std 31,-8(1) +std 0,16(1) +blr + +.section .text,"ax",@progbits,unique,1 +.globl _restgpr0_14,_restgpr0_15,_restgpr0_16,_restgpr0_17,_restgpr0_18,_restgpr0_19,_restgpr0_20,_restgpr0_21,_restgpr0_22,_restgpr0_23,_restgpr0_24,_restgpr0_25,_restgpr0_26,_restgpr0_27,_restgpr0_28,_restgpr0_29,_restgpr0_30,_restgpr0_31 +.hidden _restgpr0_14,_restgpr0_15,_restgpr0_16,_restgpr0_17,_restgpr0_18,_restgpr0_19,_restgpr0_20,_restgpr0_21,_restgpr0_22,_restgpr0_23,_restgpr0_24,_restgpr0_25,_restgpr0_26,_restgpr0_27,_restgpr0_28,_restgpr0_29,_restgpr0_30,_restgpr0_31 +_restgpr0_14: ld 14,-144(1) +_restgpr0_15: ld 15,-136(1) +_restgpr0_16: ld 16,-128(1) +_restgpr0_17: ld 17,-120(1) +_restgpr0_18: ld 18,-112(1) +_restgpr0_19: ld 19,-104(1) +_restgpr0_20: ld 20,-96(1) +_restgpr0_21: ld 21,-88(1) +_restgpr0_22: ld 22,-80(1) +_restgpr0_23: ld 23,-72(1) +_restgpr0_24: ld 24,-64(1) +_restgpr0_25: ld 25,-56(1) +_restgpr0_26: ld 26,-48(1) +_restgpr0_27: ld 27,-40(1) +_restgpr0_28: ld 28,-32(1) +_restgpr0_29: ld 0,16(1) +ld 29,-24(1) +mtlr 0 +ld 30,-16(1) +ld 31,-8(1) +blr +_restgpr0_30: ld 30,-16(1) +_restgpr0_31: ld 0,16(1) +ld 31,-8(1) +mtlr 0 +blr +)"; + +static constexpr char savegpr1[] = R"( +.section .text,"ax",@progbits,unique,0 +.globl _savegpr1_14,_savegpr1_15,_savegpr1_16,_savegpr1_17,_savegpr1_18,_savegpr1_19,_savegpr1_20,_savegpr1_21,_savegpr1_22,_savegpr1_23,_savegpr1_24,_savegpr1_25,_savegpr1_26,_savegpr1_27,_savegpr1_28,_savegpr1_29,_savegpr1_30,_savegpr1_31 +.hidden _savegpr1_14,_savegpr1_15,_savegpr1_16,_savegpr1_17,_savegpr1_18,_savegpr1_19,_savegpr1_20,_savegpr1_21,_savegpr1_22,_savegpr1_23,_savegpr1_24,_savegpr1_25,_savegpr1_26,_savegpr1_27,_savegpr1_28,_savegpr1_29,_savegpr1_30,_savegpr1_31 +_savegpr1_14: std 14,-144(12) +_savegpr1_15: std 15,-136(12) +_savegpr1_16: std 16,-128(12) +_savegpr1_17: std 17,-120(12) +_savegpr1_18: std 18,-112(12) +_savegpr1_19: std 19,-104(12) +_savegpr1_20: std 20,-96(12) +_savegpr1_21: std 21,-88(12) +_savegpr1_22: std 22,-80(12) +_savegpr1_23: std 23,-72(12) +_savegpr1_24: std 24,-64(12) +_savegpr1_25: std 25,-56(12) +_savegpr1_26: std 26,-48(12) +_savegpr1_27: std 27,-40(12) +_savegpr1_28: std 28,-32(12) +_savegpr1_29: std 29,-24(12) +_savegpr1_30: std 30,-16(12) +_savegpr1_31: std 31,-8(12) +blr + +.section .text,"ax",@progbits,unique,1 +.globl _restgpr1_14,_restgpr1_15,_restgpr1_16,_restgpr1_17,_restgpr1_18,_restgpr1_19,_restgpr1_20,_restgpr1_21,_restgpr1_22,_restgpr1_23,_restgpr1_24,_restgpr1_25,_restgpr1_26,_restgpr1_27,_restgpr1_28,_restgpr1_29,_restgpr1_30,_restgpr1_31 +.hidden _restgpr1_14,_restgpr1_15,_restgpr1_16,_restgpr1_17,_restgpr1_18,_restgpr1_19,_restgpr1_20,_restgpr1_21,_restgpr1_22,_restgpr1_23,_restgpr1_24,_restgpr1_25,_restgpr1_26,_restgpr1_27,_restgpr1_28,_restgpr1_29,_restgpr1_30,_restgpr1_31 +_restgpr1_14: ld 14,-144(12) +_restgpr1_15: ld 15,-136(12) +_restgpr1_16: ld 16,-128(12) +_restgpr1_17: ld 17,-120(12) +_restgpr1_18: ld 18,-112(12) +_restgpr1_19: ld 19,-104(12) +_restgpr1_20: ld 20,-96(12) +_restgpr1_21: ld 21,-88(12) +_restgpr1_22: ld 22,-80(12) +_restgpr1_23: ld 23,-72(12) +_restgpr1_24: ld 24,-64(12) +_restgpr1_25: ld 25,-56(12) +_restgpr1_26: ld 26,-48(12) +_restgpr1_27: ld 27,-40(12) +_restgpr1_28: ld 28,-32(12) +_restgpr1_29: ld 29,-24(12) +_restgpr1_30: ld 30,-16(12) +_restgpr1_31: ld 31,-8(12) +blr +)"; + +// clang-format on + +LazyObjFile *elf::getPPC64SaveRestFile(StringRef name) { + const char *source; + if (name == "savegpr0") + source = savegpr0; + else if (name == "savegpr1") + source = savegpr1; + else + assert(0 && "invalid name"); + + StringRef tripleName = config->isLE ? "powerpc64le-unknown-linux-gnu" + : "powerpc64-unknown-linux-gnu"; + Triple triple(tripleName); + std::string err; + const Target *target = TargetRegistry::lookupTarget("", triple, err); + if (!target) + return nullptr; + std::unique_ptr buffer = MemoryBuffer::getMemBuffer(source); + SourceMgr mgr; + mgr.AddNewSourceBuffer(std::move(buffer), SMLoc()); + + MCTargetOptions mcOptions; + std::unique_ptr mri(target->createMCRegInfo(tripleName)); + std::unique_ptr mai( + target->createMCAsmInfo(*mri, tripleName, mcOptions)); + MCObjectFileInfo mofi; + MCContext ctx(mai.get(), mri.get(), &mofi, &mgr, &mcOptions); + mofi.InitMCObjectFileInfo(triple, true, ctx, false); + std::unique_ptr mcii(target->createMCInstrInfo()); + std::unique_ptr sti( + target->createMCSubtargetInfo(tripleName, "", "")); + + SmallString<0> buf; + raw_svector_ostream os(buf); + std::unique_ptr ce( + target->createMCCodeEmitter(*mcii, *mri, ctx)); + MCAsmBackend *mab = target->createMCAsmBackend(*sti, *mri, mcOptions); + std::unique_ptr str(target->createMCObjectStreamer( + triple, ctx, std::unique_ptr(mab), + mab->createObjectWriter(os), std::move(ce), *sti, + /*RelaxAll=*/false, /*IncrementalLinkerCompatible=*/false, + /*DWARFMustBeAtTheEnd=*/false)); + + std::unique_ptr parser(createMCAsmParser(mgr, ctx, *str, *mai)); + std::unique_ptr tap( + target->createMCAsmParser(*sti, *parser, *mcii, mcOptions)); + parser->setTargetParser(*tap); + if (parser->Run(false)) + return nullptr; + + auto *mb = make>( + MemoryBuffer::getMemBufferCopy(os.str())); + return make(**mb, "", 0); +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -18,6 +18,7 @@ Arch/MSP430.cpp Arch/PPC.cpp Arch/PPC64.cpp + Arch/PPC64SaveRest.cpp Arch/RISCV.cpp Arch/SPARCV9.cpp Arch/X86.cpp @@ -55,6 +56,7 @@ Demangle LTO MC + MCParser Object Option Passes diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1851,6 +1851,32 @@ parseFile(files[i]); } + if (config->emachine == EM_PPC64) { + bool savegpr0 = false, savegpr1 = false; + char name[32]; + for (int i = 14; i < 32; ++i) { + format("_savegpr0_%d", i).snprint(name, sizeof(name)); + if (symtab->find(name)) + savegpr0 = true; + format("_restgpr0_%d", i).snprint(name, sizeof(name)); + if (symtab->find(name)) + savegpr0 = true; + + format("_savegpr1_%d", i).snprint(name, sizeof(name)); + if (symtab->find(name)) + savegpr1 = true; + format("_restgpr1_%d", i).snprint(name, sizeof(name)); + if (symtab->find(name)) + savegpr1 = true; + } + if (savegpr0) + if (LazyObjFile *file = getPPC64SaveRestFile("savegpr0")) + parseFile(file); + if (savegpr1) + if (LazyObjFile *file = getPPC64SaveRestFile("savegpr1")) + parseFile(file); + } + // Now that we have every file, we can decide if we will need a // dynamic symbol table. // We need one if we were asked to export dynamic symbols or if we are diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -390,6 +390,8 @@ std::string replaceThinLTOSuffix(StringRef path); +LazyObjFile *getPPC64SaveRestFile(StringRef name); + extern std::vector archiveFiles; extern std::vector binaryFiles; extern std::vector bitcodeFiles; diff --git a/lld/test/ELF/ppc64-savegpr0.s b/lld/test/ELF/ppc64-savegpr0.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-savegpr0.s @@ -0,0 +1,60 @@ +# REQUIRES: ppc +## _savegpr0_{14..31} and _restgpr0_{14..31} can be synthesized. + +# RUN: llvm-mc -filetype=obj -triple=ppc64 %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d --no-show-raw-insn %t14 | FileCheck %s +# RUN: llvm-readelf -s %t14 | FileCheck --check-prefix=NM %s + +# RUN: echo 'bl _restgpr0_14' | llvm-mc -filetype=obj -triple=ppc64le - -o %t14r.o +# RUN: ld.lld -shared %t14r.o -o %t14r.so +# RUN: llvm-objdump -d --no-show-raw-insn %t14r.so | FileCheck %s + +# RUN: echo 'bl _savegpr0_31' | llvm-mc -filetype=obj -triple=ppc64le - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d --no-show-raw-insn %t31 | FileCheck %s + +# RUN: echo 'bl _savegpr0_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +# NM: NOTYPE LOCAL HIDDEN {{.*}} _savegpr0_14 + +# CHECK: <_savegpr0_14>: +# CHECK-NEXT: std 14, -144(1) +# CHECK-EMPTY: +# CHECK-NEXT: <_savegpr0_15>: +# CHECK-NEXT: std 15, -136(1) +# CHECK-EMPTY: + +# CHECK: <_savegpr0_31>: +# CHECK-NEXT: std 31, -8(1) +# CHECK-NEXT: std 0, 16(1) +# CHECK-NEXT: blr +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr0_14>: +# CHECK-NEXT: ld 14, -144(1) +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr0_15>: +# CHECK-NEXT: ld 15, -136(1) +# CHECK-EMPTY: + +# CHECK: <_restgpr0_29>: +# CHECK-NEXT: ld 0, 16(1) +# CHECK-NEXT: ld 29, -24(1) +# CHECK-NEXT: mtlr 0 +# CHECK-NEXT: ld 30, -16(1) +# CHECK-NEXT: ld 31, -8(1) +# CHECK-NEXT: blr +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr0_30>: +# CHECK-NEXT: ld 30, -16(1) +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr0_31>: +# CHECK-NEXT: ld 0, 16(1) +# CHECK-NEXT: ld 31, -8(1) +# CHECK-NEXT: mtlr 0 +# CHECK-NEXT: blr + +.globl _start +_start: + bl _savegpr0_14 diff --git a/lld/test/ELF/ppc64-savegpr1.s b/lld/test/ELF/ppc64-savegpr1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-savegpr1.s @@ -0,0 +1,45 @@ +# REQUIRES: ppc +## _savegpr1_{14..31} and _restgpr1_{14..31} can be synthesized. + +# RUN: llvm-mc -filetype=obj -triple=ppc64 %s -o %t14.o +# RUN: ld.lld %t14.o -o %t14 +# RUN: llvm-objdump -d --no-show-raw-insn %t14 | FileCheck %s + +# RUN: echo 'bl _restgpr1_14' | llvm-mc -filetype=obj -triple=ppc64le - -o %t14r.o +# RUN: ld.lld -shared %t14r.o -o %t14r.so +# RUN: llvm-objdump -d --no-show-raw-insn %t14r.so | FileCheck %s + +# RUN: echo 'bl _savegpr1_31' | llvm-mc -filetype=obj -triple=ppc64le - -o %t31.o +# RUN: ld.lld %t31.o -o %t31 +# RUN: llvm-objdump -d --no-show-raw-insn %t31 | FileCheck %s + +# RUN: echo 'bl _savegpr1_32' | llvm-mc -filetype=obj -triple=ppc64le - -o %t32.o +# RUN: not ld.lld %t32.o -o /dev/null + +# NM: NOTYPE LOCAL HIDDEN {{.*}} _savegpr1_14 + +# CHECK: <_savegpr1_14>: +# CHECK-NEXT: std 14, -144(12) +# CHECK-EMPTY: +# CHECK-NEXT: <_savegpr1_15>: +# CHECK-NEXT: std 15, -136(12) +# CHECK-EMPTY: + +# CHECK: <_savegpr1_31>: +# CHECK-NEXT: std 31, -8(12) +# CHECK-NEXT: blr +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr1_14>: +# CHECK-NEXT: ld 14, -144(12) +# CHECK-EMPTY: +# CHECK-NEXT: <_restgpr1_15>: +# CHECK-NEXT: ld 15, -136(12) +# CHECK-EMPTY: + +# CHECK: <_restgpr1_31>: +# CHECK-NEXT: ld 31, -8(12) +# CHECK-NEXT: blr + +.globl _start +_start: + bl _savegpr1_14 diff --git a/llvm/utils/gn/secondary/lld/ELF/BUILD.gn b/llvm/utils/gn/secondary/lld/ELF/BUILD.gn --- a/llvm/utils/gn/secondary/lld/ELF/BUILD.gn +++ b/llvm/utils/gn/secondary/lld/ELF/BUILD.gn @@ -35,6 +35,7 @@ "Arch/MipsArchTree.cpp", "Arch/PPC.cpp", "Arch/PPC64.cpp", + "Arch/PPC64SaveRest.cpp", "Arch/RISCV.cpp", "Arch/SPARCV9.cpp", "Arch/X86.cpp",