class TargetLoweringObjectFileMachO : public TargetLoweringObjectFile {
public:
virtual ~TargetLoweringObjectFileMachO() {}
+ TargetLoweringObjectFileMachO();
/// Extract the dependent library name from a linker option string. Returns
/// StringRef() if the option does not specify a library.
MCSymbol *getCFIPersonalitySymbol(const GlobalValue *GV, Mangler &Mang,
const TargetMachine &TM,
MachineModuleInfo *MMI) const override;
+
+ /// Get MachO PC relative GOT entry relocation
+ const MCExpr *getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
+ const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI,
+ MCStreamer &Streamer) const override;
};
class MCSymbol;
class MCSymbolRefExpr;
class MCStreamer;
+ class MCValue;
class ConstantExpr;
class GlobalValue;
class TargetMachine;
class TargetLoweringObjectFile : public MCObjectFileInfo {
MCContext *Ctx;
- const DataLayout *DL;
TargetLoweringObjectFile(
const TargetLoweringObjectFile&) = delete;
void operator=(const TargetLoweringObjectFile&) = delete;
protected:
+ const DataLayout *DL;
bool SupportIndirectSymViaGOTPCRel;
bool SupportGOTPCRelWithOffset;
/// \brief Get the target specific PC relative GOT entry relocation
virtual const MCExpr *getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
+ const MCValue &MV,
int64_t Offset,
+ MachineModuleInfo *MMI,
MCStreamer &Streamer) const {
return nullptr;
}
//
// gotpcrelcst := <offset from @foo base> + <cst>
//
- // Only encode <cst> if the target supports it.
+ // If gotpcrelcst is positive it means that we can safely fold the pc rel
+ // displacement into the GOTPCREL. We can also can have an extra offset <cst>
+ // if the target knows how to encode it.
//
int64_t GOTPCRelCst = Offset + MV.getConstant();
if (GOTPCRelCst < 0)
unsigned NumUses = Result.second;
const GlobalValue *FinalGV = dyn_cast<GlobalValue>(GV->getOperand(0));
const MCSymbol *FinalSym = AP.getSymbol(FinalGV);
- *ME = AP.getObjFileLowering().getIndirectSymViaGOTPCRel(FinalSym,
- GOTPCRelCst,
- AP.OutStreamer);
+ *ME = AP.getObjFileLowering().getIndirectSymViaGOTPCRel(
+ FinalSym, MV, Offset, AP.MMI, AP.OutStreamer);
// Update GOT equivalent usage information
--NumUses;
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/MCValue.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/ErrorHandling.h"
// MachO
//===----------------------------------------------------------------------===//
+TargetLoweringObjectFileMachO::TargetLoweringObjectFileMachO()
+ : TargetLoweringObjectFile() {
+ SupportIndirectSymViaGOTPCRel = true;
+}
+
/// getDepLibFromLinkerOpt - Extract the dependent library name from a linker
/// option string. Returns StringRef() if the option does not specify a library.
StringRef TargetLoweringObjectFileMachO::
return SSym;
}
+const MCExpr *TargetLoweringObjectFileMachO::getIndirectSymViaGOTPCRel(
+ const MCSymbol *Sym, const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI, MCStreamer &Streamer) const {
+ // Although MachO 32-bit targets do not explictly have a GOTPCREL relocation
+ // as 64-bit do, we replace the GOT equivalent by accessing the final symbol
+ // through a non_lazy_ptr stub instead. One advantage is that it allows the
+ // computation of deltas to final external symbols. Example:
+ //
+ // _extgotequiv:
+ // .long _extfoo
+ //
+ // _delta:
+ // .long _extgotequiv-_delta
+ //
+ // is transformed to:
+ //
+ // _delta:
+ // .long L_extfoo$non_lazy_ptr-(_delta+0)
+ //
+ // .section __IMPORT,__pointers,non_lazy_symbol_pointers
+ // L_extfoo$non_lazy_ptr:
+ // .indirect_symbol _extfoo
+ // .long 0
+ //
+ MachineModuleInfoMachO &MachOMMI =
+ MMI->getObjFileInfo<MachineModuleInfoMachO>();
+ MCContext &Ctx = getContext();
+
+ // The offset must consider the original displacement from the base symbol
+ // since 32-bit targets don't have a GOTPCREL to fold the PC displacement.
+ Offset = -MV.getConstant();
+ const MCSymbol *BaseSym = &MV.getSymB()->getSymbol();
+
+ // Access the final symbol via sym$non_lazy_ptr and generate the appropriated
+ // non_lazy_ptr stubs.
+ SmallString<128> Name;
+ StringRef Suffix = "$non_lazy_ptr";
+ Name += DL->getPrivateGlobalPrefix();
+ Name += Sym->getName();
+ Name += Suffix;
+ MCSymbol *Stub = Ctx.GetOrCreateSymbol(Name);
+
+ MachineModuleInfoImpl::StubValueTy &StubSym = MachOMMI.getGVStubEntry(Stub);
+ if (!StubSym.getPointer())
+ StubSym = MachineModuleInfoImpl::
+ StubValueTy(const_cast<MCSymbol *>(Sym), true /* access indirectly */);
+
+ const MCExpr *BSymExpr =
+ MCSymbolRefExpr::Create(BaseSym, MCSymbolRefExpr::VK_None, Ctx);
+ const MCExpr *LHS =
+ MCSymbolRefExpr::Create(Stub, MCSymbolRefExpr::VK_None, Ctx);
+
+ if (!Offset)
+ return MCBinaryExpr::CreateSub(LHS, BSymExpr, Ctx);
+
+ const MCExpr *RHS =
+ MCBinaryExpr::CreateAdd(BSymExpr, MCConstantExpr::Create(Offset, Ctx), Ctx);
+ return MCBinaryExpr::CreateSub(LHS, RHS, Ctx);
+}
+
//===----------------------------------------------------------------------===//
// COFF
//===----------------------------------------------------------------------===//
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCValue.h"
#include "llvm/Support/Dwarf.h"
using namespace llvm;
using namespace dwarf;
AArch64_MachoTargetObjectFile::AArch64_MachoTargetObjectFile()
: TargetLoweringObjectFileMachO() {
- SupportIndirectSymViaGOTPCRel = true;
SupportGOTPCRelWithOffset = false;
}
}
const MCExpr *AArch64_MachoTargetObjectFile::getIndirectSymViaGOTPCRel(
- const MCSymbol *Sym, int64_t Offset, MCStreamer &Streamer) const {
+ const MCSymbol *Sym, const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI, MCStreamer &Streamer) const {
+ assert((Offset+MV.getConstant() == 0) &&
+ "Arch64 does not support GOT PC rel with extra offset");
// On ARM64 Darwin, we can reference symbols with foo@GOT-., which
// is an indirect pc-relative reference.
const MCExpr *Res =
const TargetMachine &TM,
MachineModuleInfo *MMI) const override;
- const MCExpr *
- getIndirectSymViaGOTPCRel(const MCSymbol *Sym, int64_t Offset,
- MCStreamer &Streamer) const override;
+ const MCExpr *getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
+ const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI,
+ MCStreamer &Streamer) const override;
};
} // end namespace llvm
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCSectionCOFF.h"
#include "llvm/MC/MCSectionELF.h"
+#include "llvm/MC/MCValue.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Target/TargetLowering.h"
using namespace llvm;
using namespace dwarf;
-X86_64MachoTargetObjectFile::X86_64MachoTargetObjectFile()
- : TargetLoweringObjectFileMachO() {
- SupportIndirectSymViaGOTPCRel = true;
-}
-
const MCExpr *X86_64MachoTargetObjectFile::getTTypeGlobalReference(
const GlobalValue *GV, unsigned Encoding, Mangler &Mang,
const TargetMachine &TM, MachineModuleInfo *MMI,
}
const MCExpr *X86_64MachoTargetObjectFile::getIndirectSymViaGOTPCRel(
- const MCSymbol *Sym, int64_t Offset, MCStreamer &Streamer) const {
+ const MCSymbol *Sym, const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI, MCStreamer &Streamer) const {
// On Darwin/X86-64, we need to use foo@GOTPCREL+4 to access the got entry
// from a data section. In case there's an additional offset, then use
// foo@GOTPCREL+4+<offset>.
+ unsigned FinalOff = Offset+MV.getConstant()+4;
const MCExpr *Res =
MCSymbolRefExpr::Create(Sym, MCSymbolRefExpr::VK_GOTPCREL, getContext());
- const MCExpr *Off = MCConstantExpr::Create(Offset+4, getContext());
+ const MCExpr *Off = MCConstantExpr::Create(FinalOff, getContext());
return MCBinaryExpr::CreateAdd(Res, Off, getContext());
}
/// x86-64.
class X86_64MachoTargetObjectFile : public TargetLoweringObjectFileMachO {
public:
- X86_64MachoTargetObjectFile();
-
const MCExpr *
getTTypeGlobalReference(const GlobalValue *GV, unsigned Encoding,
Mangler &Mang, const TargetMachine &TM,
const TargetMachine &TM,
MachineModuleInfo *MMI) const override;
- const MCExpr *
- getIndirectSymViaGOTPCRel(const MCSymbol *Sym, int64_t Offset,
- MCStreamer &Streamer) const override;
+ const MCExpr *getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
+ const MCValue &MV, int64_t Offset,
+ MachineModuleInfo *MMI,
+ MCStreamer &Streamer) const override;
};
/// \brief This implemenatation is used for X86 ELF targets that don't
--- /dev/null
+; RUN: llc -mtriple=i386-apple-darwin %s -o %t
+; RUN: FileCheck %s < %t
+; RUN: FileCheck %s -check-prefix=GOT-EQUIV < %t
+; RUN: llc -mtriple=arm-apple-darwin %s -o %t
+; RUN: FileCheck %s < %t
+; RUN: FileCheck %s -check-prefix=GOT-EQUIV < %t
+
+; GOT equivalent globals references can be replaced by the GOT entry of the
+; final symbol instead.
+
+%struct.data = type { i32, %struct.anon }
+%struct.anon = type { i32, i32 }
+
+; Check that these got equivalent symbols are never emitted or used
+; GOT-EQUIV-NOT: _localgotequiv
+; GOT-EQUIV-NOT: _extgotequiv
+@localfoo = global i32 42
+@localgotequiv = private unnamed_addr constant i32* @localfoo
+
+@extfoo = external global i32
+@extgotequiv = private unnamed_addr constant i32* @extfoo
+
+; Don't replace GOT equivalent usage within instructions and emit the GOT
+; equivalent since it can't be replaced by the GOT entry. @bargotequiv is
+; used by an instruction inside @t0.
+;
+; CHECK: l_bargotequiv:
+; CHECK-NEXT: .long _extbar
+@extbar = external global i32
+@bargotequiv = private unnamed_addr constant i32* @extbar
+
+@table = global [4 x %struct.data] [
+; CHECK-LABEL: _table
+ %struct.data { i32 1, %struct.anon { i32 2, i32 3 } },
+; Test GOT equivalent usage inside nested constant arrays.
+; CHECK: .long 5
+; CHECK-NOT: l_localgotequiv-(_table+20)
+; CHECK-NEXT: L_localfoo$non_lazy_ptr-(_table+20)
+ %struct.data { i32 4, %struct.anon { i32 5,
+ i32 sub (i32 ptrtoint (i32** @localgotequiv to i32),
+ i32 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i32 1, i32 1, i32 1) to i32))}
+ },
+; CHECK: .long 5
+; CHECK-NOT: l_extgotequiv-(_table+32)
+; CHECK-NEXT: L_extfoo$non_lazy_ptr-(_table+32)
+ %struct.data { i32 4, %struct.anon { i32 5,
+ i32 sub (i32 ptrtoint (i32** @extgotequiv to i32),
+ i32 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i32 2, i32 1, i32 1) to i32))}
+ },
+; Test support for arbitrary constants into the GOTPCREL offset
+; CHECK: .long 5
+; CHECK-NOT: (l_extgotequiv-(_table+44))+24
+; CHECK-NEXT: L_extfoo$non_lazy_ptr-(_table+20)
+ %struct.data { i32 4, %struct.anon { i32 5,
+ i32 add (i32 sub (i32 ptrtoint (i32** @extgotequiv to i32),
+ i32 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i32 3, i32 1, i32 1) to i32)),
+ i32 24)}
+ }
+], align 16
+
+; Test multiple uses of GOT equivalents.
+; CHECK-LABEL: _delta
+; CHECK: .long L_extfoo$non_lazy_ptr-_delta
+@delta = global i32 sub (i32 ptrtoint (i32** @extgotequiv to i32),
+ i32 ptrtoint (i32* @delta to i32))
+
+; CHECK-LABEL: _deltaplus:
+; CHECK: .long L_localfoo$non_lazy_ptr-(_deltaplus-55)
+@deltaplus = global i32 add (i32 sub (i32 ptrtoint (i32** @localgotequiv to i32),
+ i32 ptrtoint (i32* @deltaplus to i32)),
+ i32 55)
+
+define i32 @t0(i32 %a) {
+ %x = add i32 sub (i32 ptrtoint (i32** @bargotequiv to i32),
+ i32 ptrtoint (i32 (i32)* @t0 to i32)), %a
+ ret i32 %x
+}