1
0
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:
Peter Collingbourne 2016-04-22 20:40:10 +00:00
parent 29f7cc186f
commit df3f55c3ab
14 changed files with 155 additions and 58 deletions

View File

@ -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.

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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_;

View File

@ -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;

View File

@ -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) {

View File

@ -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.

View 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

View 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

View 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

View File

@ -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"

View File

@ -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))