mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-19 02:52:53 +02:00
CodeGen: Use PLT relocations for relative references to unnamed_addr functions.
The relative vtable ABI (PR26723) needs PLT relocations to refer to virtual functions defined in other DSOs. The unnamed_addr attribute means that the function's address is not significant, so we're allowed to substitute it with the address of a PLT entry. Also includes a bonus feature: addends for COFF image-relative references. Differential Revision: http://reviews.llvm.org/D17938 llvm-svn: 267211
This commit is contained in:
parent
29f7cc186f
commit
df3f55c3ab
@ -21,14 +21,21 @@
|
||||
#define LLVM_ANALYSIS_CONSTANTFOLDING_H
|
||||
|
||||
namespace llvm {
|
||||
class APInt;
|
||||
template <typename T> class ArrayRef;
|
||||
class Constant;
|
||||
class ConstantExpr;
|
||||
class Instruction;
|
||||
class DataLayout;
|
||||
class TargetLibraryInfo;
|
||||
class Function;
|
||||
class GlobalValue;
|
||||
class Instruction;
|
||||
class TargetLibraryInfo;
|
||||
class Type;
|
||||
template <typename T> class ArrayRef;
|
||||
|
||||
/// If this constant is a constant offset from a global, return the global and
|
||||
/// the constant. Because of constantexprs, this function is recursive.
|
||||
bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
|
||||
const DataLayout &DL);
|
||||
|
||||
/// ConstantFoldInstruction - Try to constant fold the specified instruction.
|
||||
/// If successful, the constant result is returned, if not, null is returned.
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
|
||||
#define LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
|
||||
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/SectionKind.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
|
||||
@ -22,7 +23,6 @@ namespace llvm {
|
||||
class MachineModuleInfo;
|
||||
class Mangler;
|
||||
class MCAsmInfo;
|
||||
class MCExpr;
|
||||
class MCSection;
|
||||
class MCSectionMachO;
|
||||
class MCSymbol;
|
||||
@ -35,6 +35,10 @@ class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
|
||||
bool UseInitArray;
|
||||
mutable unsigned NextUniqueID = 0;
|
||||
|
||||
protected:
|
||||
MCSymbolRefExpr::VariantKind PLTRelativeVariantKind =
|
||||
MCSymbolRefExpr::VK_None;
|
||||
|
||||
public:
|
||||
TargetLoweringObjectFileELF() : UseInitArray(false) {}
|
||||
|
||||
@ -81,6 +85,10 @@ public:
|
||||
const MCSymbol *KeySym) const override;
|
||||
MCSection *getStaticDtorSection(unsigned Priority,
|
||||
const MCSymbol *KeySym) const override;
|
||||
|
||||
const MCExpr *lowerRelativeReference(const GlobalValue *LHS,
|
||||
const GlobalValue *RHS, Mangler &Mang,
|
||||
const TargetMachine &TM) const override;
|
||||
};
|
||||
|
||||
|
||||
|
@ -155,8 +155,8 @@ public:
|
||||
virtual const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const;
|
||||
|
||||
virtual const MCExpr *
|
||||
getExecutableRelativeSymbol(const ConstantExpr *CE, Mangler &Mang,
|
||||
const TargetMachine &TM) const {
|
||||
lowerRelativeReference(const GlobalValue *LHS, const GlobalValue *RHS,
|
||||
Mangler &Mang, const TargetMachine &TM) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -231,10 +231,12 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
|
||||
return ConstantVector::get(Result);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
/// If this constant is a constant offset from a global, return the global and
|
||||
/// the constant. Because of constantexprs, this function is recursive.
|
||||
bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
|
||||
const DataLayout &DL) {
|
||||
bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
||||
APInt &Offset, const DataLayout &DL) {
|
||||
// Trivial case, constant is the global.
|
||||
if ((GV = dyn_cast<GlobalValue>(C))) {
|
||||
unsigned BitWidth = DL.getPointerTypeSizeInBits(GV->getType());
|
||||
@ -271,6 +273,8 @@ bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// Recursive helper to read bits out of global. C is the constant being copied
|
||||
/// out of. ByteOffset is an offset into C. CurPtr is the pointer to copy
|
||||
/// results into and BytesLeft is the number of bytes left in
|
||||
|
@ -1763,10 +1763,6 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
|
||||
llvm_unreachable("Unknown constant value to lower!");
|
||||
}
|
||||
|
||||
if (const MCExpr *RelocExpr
|
||||
= getObjFileLowering().getExecutableRelativeSymbol(CE, *Mang, TM))
|
||||
return RelocExpr;
|
||||
|
||||
switch (CE->getOpcode()) {
|
||||
default:
|
||||
// If the code isn't optimized, there may be outstanding folding
|
||||
@ -1842,10 +1838,34 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
|
||||
return MCBinaryExpr::createAnd(OpExpr, MaskExpr, Ctx);
|
||||
}
|
||||
|
||||
case Instruction::Sub: {
|
||||
GlobalValue *LHSGV;
|
||||
APInt LHSOffset;
|
||||
if (IsConstantOffsetFromGlobal(CE->getOperand(0), LHSGV, LHSOffset,
|
||||
getDataLayout())) {
|
||||
GlobalValue *RHSGV;
|
||||
APInt RHSOffset;
|
||||
if (IsConstantOffsetFromGlobal(CE->getOperand(1), RHSGV, RHSOffset,
|
||||
getDataLayout())) {
|
||||
const MCExpr *RelocExpr = getObjFileLowering().lowerRelativeReference(
|
||||
LHSGV, RHSGV, *Mang, TM);
|
||||
if (!RelocExpr)
|
||||
RelocExpr = MCBinaryExpr::createSub(
|
||||
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx),
|
||||
MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
|
||||
int64_t Addend = (LHSOffset - RHSOffset).getSExtValue();
|
||||
if (Addend != 0)
|
||||
RelocExpr = MCBinaryExpr::createAdd(
|
||||
RelocExpr, MCConstantExpr::create(Addend, Ctx), Ctx);
|
||||
return RelocExpr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else fallthrough
|
||||
|
||||
// The MC library also has a right-shift operator, but it isn't consistently
|
||||
// signed or unsigned between different targets.
|
||||
case Instruction::Add:
|
||||
case Instruction::Sub:
|
||||
case Instruction::Mul:
|
||||
case Instruction::SDiv:
|
||||
case Instruction::SRem:
|
||||
|
@ -438,6 +438,27 @@ MCSection *TargetLoweringObjectFileELF::getStaticDtorSection(
|
||||
KeySym);
|
||||
}
|
||||
|
||||
const MCExpr *TargetLoweringObjectFileELF::lowerRelativeReference(
|
||||
const GlobalValue *LHS, const GlobalValue *RHS, Mangler &Mang,
|
||||
const TargetMachine &TM) const {
|
||||
// We may only use a PLT-relative relocation to refer to unnamed_addr
|
||||
// functions.
|
||||
if (!LHS->hasUnnamedAddr() || !LHS->getValueType()->isFunctionTy())
|
||||
return nullptr;
|
||||
|
||||
// Basic sanity checks.
|
||||
if (LHS->getType()->getPointerAddressSpace() != 0 ||
|
||||
RHS->getType()->getPointerAddressSpace() != 0 || LHS->isThreadLocal() ||
|
||||
RHS->isThreadLocal())
|
||||
return nullptr;
|
||||
|
||||
return MCBinaryExpr::createSub(
|
||||
MCSymbolRefExpr::create(TM.getSymbol(LHS, Mang), PLTRelativeVariantKind,
|
||||
getContext()),
|
||||
MCSymbolRefExpr::create(TM.getSymbol(RHS, Mang), getContext()),
|
||||
getContext());
|
||||
}
|
||||
|
||||
void
|
||||
TargetLoweringObjectFileELF::InitializeELF(bool UseInitArray_) {
|
||||
UseInitArray = UseInitArray_;
|
||||
|
@ -21,10 +21,10 @@ class ARMElfTargetObjectFile : public TargetLoweringObjectFileELF {
|
||||
protected:
|
||||
const MCSection *AttributesSection;
|
||||
public:
|
||||
ARMElfTargetObjectFile() :
|
||||
TargetLoweringObjectFileELF(),
|
||||
AttributesSection(nullptr)
|
||||
{}
|
||||
ARMElfTargetObjectFile()
|
||||
: TargetLoweringObjectFileELF(), AttributesSection(nullptr) {
|
||||
PLTRelativeVariantKind = MCSymbolRefExpr::VK_ARM_PREL31;
|
||||
}
|
||||
|
||||
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
|
||||
|
||||
|
@ -73,53 +73,30 @@ X86LinuxNaClTargetObjectFile::Initialize(MCContext &Ctx,
|
||||
InitializeELF(TM.Options.UseInitArray);
|
||||
}
|
||||
|
||||
const MCExpr *X86WindowsTargetObjectFile::getExecutableRelativeSymbol(
|
||||
const ConstantExpr *CE, Mangler &Mang, const TargetMachine &TM) const {
|
||||
// We are looking for the difference of two symbols, need a subtraction
|
||||
// operation.
|
||||
const SubOperator *Sub = dyn_cast<SubOperator>(CE);
|
||||
if (!Sub)
|
||||
return nullptr;
|
||||
|
||||
// Symbols must first be numbers before we can subtract them, we need to see a
|
||||
// ptrtoint on both subtraction operands.
|
||||
const PtrToIntOperator *SubLHS =
|
||||
dyn_cast<PtrToIntOperator>(Sub->getOperand(0));
|
||||
const PtrToIntOperator *SubRHS =
|
||||
dyn_cast<PtrToIntOperator>(Sub->getOperand(1));
|
||||
if (!SubLHS || !SubRHS)
|
||||
return nullptr;
|
||||
|
||||
const MCExpr *X86WindowsTargetObjectFile::lowerRelativeReference(
|
||||
const GlobalValue *LHS, const GlobalValue *RHS, Mangler &Mang,
|
||||
const TargetMachine &TM) const {
|
||||
// Our symbols should exist in address space zero, cowardly no-op if
|
||||
// otherwise.
|
||||
if (SubLHS->getPointerAddressSpace() != 0 ||
|
||||
SubRHS->getPointerAddressSpace() != 0)
|
||||
if (LHS->getType()->getPointerAddressSpace() != 0 ||
|
||||
RHS->getType()->getPointerAddressSpace() != 0)
|
||||
return nullptr;
|
||||
|
||||
// Both ptrtoint instructions must wrap global objects:
|
||||
// - Only global variables are eligible for image relative relocations.
|
||||
// - The subtrahend refers to the special symbol __ImageBase, a GlobalVariable.
|
||||
const auto *GOLHS = dyn_cast<GlobalObject>(SubLHS->getPointerOperand());
|
||||
const auto *GVRHS = dyn_cast<GlobalVariable>(SubRHS->getPointerOperand());
|
||||
if (!GOLHS || !GVRHS)
|
||||
return nullptr;
|
||||
|
||||
// We expect __ImageBase to be a global variable without a section, externally
|
||||
// defined.
|
||||
//
|
||||
// It should look something like this: @__ImageBase = external constant i8
|
||||
if (GVRHS->isThreadLocal() || GVRHS->getName() != "__ImageBase" ||
|
||||
!GVRHS->hasExternalLinkage() || GVRHS->hasInitializer() ||
|
||||
GVRHS->hasSection())
|
||||
if (!isa<GlobalObject>(LHS) || !isa<GlobalVariable>(RHS) ||
|
||||
LHS->isThreadLocal() || RHS->isThreadLocal() ||
|
||||
RHS->getName() != "__ImageBase" || !RHS->hasExternalLinkage() ||
|
||||
cast<GlobalVariable>(RHS)->hasInitializer() || RHS->hasSection())
|
||||
return nullptr;
|
||||
|
||||
// An image-relative, thread-local, symbol makes no sense.
|
||||
if (GOLHS->isThreadLocal())
|
||||
return nullptr;
|
||||
|
||||
return MCSymbolRefExpr::create(TM.getSymbol(GOLHS, Mang),
|
||||
MCSymbolRefExpr::VK_COFF_IMGREL32,
|
||||
getContext());
|
||||
return MCSymbolRefExpr::create(
|
||||
TM.getSymbol(LHS, Mang), MCSymbolRefExpr::VK_COFF_IMGREL32, getContext());
|
||||
}
|
||||
|
||||
static std::string APIntToHexString(const APInt &AI) {
|
||||
|
@ -40,6 +40,11 @@ namespace llvm {
|
||||
/// \brief This implemenatation is used for X86 ELF targets that don't
|
||||
/// have a further specialization.
|
||||
class X86ELFTargetObjectFile : public TargetLoweringObjectFileELF {
|
||||
public:
|
||||
X86ELFTargetObjectFile() {
|
||||
PLTRelativeVariantKind = MCSymbolRefExpr::VK_PLT;
|
||||
}
|
||||
|
||||
/// \brief Describe a TLS variable address within debug info.
|
||||
const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
|
||||
};
|
||||
@ -53,8 +58,9 @@ namespace llvm {
|
||||
/// \brief This implementation is used for Windows targets on x86 and x86-64.
|
||||
class X86WindowsTargetObjectFile : public TargetLoweringObjectFileCOFF {
|
||||
const MCExpr *
|
||||
getExecutableRelativeSymbol(const ConstantExpr *CE, Mangler &Mang,
|
||||
const TargetMachine &TM) const override;
|
||||
lowerRelativeReference(const GlobalValue *LHS, const GlobalValue *RHS,
|
||||
Mangler &Mang,
|
||||
const TargetMachine &TM) const override;
|
||||
|
||||
/// \brief Given a mergeable constant with the specified size and relocation
|
||||
/// information, return a section that it should be placed in.
|
||||
|
16
test/CodeGen/ARM/plt-relative-reloc.ll
Normal file
16
test/CodeGen/ARM/plt-relative-reloc.ll
Normal file
@ -0,0 +1,16 @@
|
||||
; RUN: llc -mtriple=armv7-unknown-linux -o - %s | FileCheck %s
|
||||
|
||||
@vtable = constant [4 x i32] [i32 0,
|
||||
i32 sub (i32 ptrtoint (void ()* @fn1 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32)),
|
||||
i32 sub (i32 ptrtoint (void ()* @fn2 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32)),
|
||||
i32 sub (i32 ptrtoint (void ()* @fn3 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32))
|
||||
]
|
||||
|
||||
declare void @fn1() unnamed_addr
|
||||
declare void @fn2() unnamed_addr
|
||||
declare void @fn3()
|
||||
|
||||
; CHECK: .long 0
|
||||
; CHECK-NEXT: .long (fn1(prel31)-vtable)-4
|
||||
; CHECK-NEXT: .long (fn2(prel31)-vtable)-4
|
||||
; CHECK-NEXT: .long (fn3-vtable)-4
|
19
test/CodeGen/X86/x86-64-plt-relative-reloc.ll
Normal file
19
test/CodeGen/X86/x86-64-plt-relative-reloc.ll
Normal file
@ -0,0 +1,19 @@
|
||||
; RUN: llc -mtriple=x86_64-unknown-linux -o - %s | FileCheck %s
|
||||
|
||||
@vtable = constant [5 x i32] [i32 0,
|
||||
i32 trunc (i64 sub (i64 ptrtoint (void ()* @fn1 to i64), i64 ptrtoint (i32* getelementptr ([5 x i32], [5 x i32]* @vtable, i32 0, i32 1) to i64)) to i32),
|
||||
i32 trunc (i64 sub (i64 ptrtoint (void ()* @fn2 to i64), i64 ptrtoint (i32* getelementptr ([5 x i32], [5 x i32]* @vtable, i32 0, i32 1) to i64)) to i32),
|
||||
i32 trunc (i64 sub (i64 ptrtoint (void ()* @fn3 to i64), i64 ptrtoint (i32* getelementptr ([5 x i32], [5 x i32]* @vtable, i32 0, i32 1) to i64)) to i32),
|
||||
i32 trunc (i64 sub (i64 ptrtoint (i8* @global4 to i64), i64 ptrtoint (i32* getelementptr ([5 x i32], [5 x i32]* @vtable, i32 0, i32 1) to i64)) to i32)
|
||||
]
|
||||
|
||||
declare void @fn1() unnamed_addr
|
||||
declare void @fn2() unnamed_addr
|
||||
declare void @fn3()
|
||||
@global4 = external unnamed_addr global i8
|
||||
|
||||
; CHECK: .long 0
|
||||
; CHECK-NEXT: .long (fn1@PLT-vtable)-4
|
||||
; CHECK-NEXT: .long (fn2@PLT-vtable)-4
|
||||
; CHECK-NEXT: .long (fn3-vtable)-4
|
||||
; CHECK-NEXT: .long (global4-vtable)-4
|
16
test/CodeGen/X86/x86-plt-relative-reloc.ll
Normal file
16
test/CodeGen/X86/x86-plt-relative-reloc.ll
Normal file
@ -0,0 +1,16 @@
|
||||
; RUN: llc -mtriple=i686-unknown-linux -o - %s | FileCheck %s
|
||||
|
||||
@vtable = constant [4 x i32] [i32 0,
|
||||
i32 sub (i32 ptrtoint (void ()* @fn1 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32)),
|
||||
i32 sub (i32 ptrtoint (void ()* @fn2 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32)),
|
||||
i32 sub (i32 ptrtoint (void ()* @fn3 to i32), i32 ptrtoint (i32* getelementptr ([4 x i32], [4 x i32]* @vtable, i32 0, i32 1) to i32))
|
||||
]
|
||||
|
||||
declare void @fn1() unnamed_addr
|
||||
declare void @fn2() unnamed_addr
|
||||
declare void @fn3()
|
||||
|
||||
; CHECK: .long 0
|
||||
; CHECK-NEXT: .long (fn1@PLT-vtable)-4
|
||||
; CHECK-NEXT: .long (fn2@PLT-vtable)-4
|
||||
; CHECK-NEXT: .long (fn3-vtable)-4
|
@ -22,16 +22,19 @@
|
||||
|
||||
;;;; image base relocation
|
||||
|
||||
; CHECK: .long g3@IMGREL
|
||||
; CHECK: .long g3@IMGREL{{$}}
|
||||
@t5 = global i32 trunc(i64 sub(i64 ptrtoint(i32* @g3 to i64), i64 ptrtoint(i64** @__ImageBase to i64)) to i32), section ".fix"
|
||||
|
||||
; CHECK: .long g3@IMGREL+4{{$}}
|
||||
@t6 = global i32 trunc(i64 sub(i64 ptrtoint(i32* getelementptr (i32, i32* @g3, i32 1) to i64), i64 ptrtoint(i64** @__ImageBase to i64)) to i32), section ".fix"
|
||||
|
||||
;;;; cross-section relative with source offset
|
||||
|
||||
%struct.EEType = type { [2 x i8], i64, i32}
|
||||
|
||||
; CHECK: .long g3-(t6+16)
|
||||
@t6 = global %struct.EEType {
|
||||
; CHECK: .long (g3-t7)-16
|
||||
@t7 = global %struct.EEType {
|
||||
[2 x i8] c"\01\02",
|
||||
i64 256,
|
||||
i32 trunc(i64 sub(i64 ptrtoint(i32* @g3 to i64), i64 ptrtoint(i32* getelementptr inbounds (%struct.EEType, %struct.EEType* @t6, i32 0, i32 2) to i64)) to i32 )
|
||||
i32 trunc(i64 sub(i64 ptrtoint(i32* @g3 to i64), i64 ptrtoint(i32* getelementptr inbounds (%struct.EEType, %struct.EEType* @t7, i32 0, i32 2) to i64)) to i32 )
|
||||
}, section ".fix"
|
||||
|
@ -49,7 +49,7 @@
|
||||
; supported on x86-64 but not on ARM64
|
||||
|
||||
; CHECK: .long 5
|
||||
; CHECK-NEXT: .long (l_extgotequiv-(_table+44))+24
|
||||
; CHECK-NEXT: .long ((l_extgotequiv-_table)-44)+24
|
||||
%struct.data { i32 4, %struct.anon { i32 5,
|
||||
i32 add (i32 trunc (i64 sub (i64 ptrtoint (i32** @extgotequiv to i64),
|
||||
i64 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data], [4 x %struct.data]* @table, i32 0, i64 3, i32 1, i32 1) to i64))
|
||||
|
Loading…
Reference in New Issue
Block a user