forked from OSchip/llvm-project
[llvm][IR] Add dso_local_equivalent Constant
The `dso_local_equivalent` constant is a wrapper for functions that represents a value which is functionally equivalent to the global passed to this. That is, if this accepts a function, calling this constant should have the same effects as calling the function directly. This could be a direct reference to the function, the `@plt` modifier on X86/AArch64, a thunk, or anything that's equivalent to the resolved function as a call target. When lowered, the returned address must have a constant offset at link time from some other symbol defined within the same binary. The address of this value is also insignificant. The name is leveraged from `dso_local` where use of a function or variable is resolved to a symbol in the same linkage unit. In this patch: - Addition of `dso_local_equivalent` and handling it - Update Constant::needsRelocation() to strip constant inbound GEPs and take advantage of `dso_local_equivalent` for relative references This is useful for the [Relative VTables C++ ABI](https://reviews.llvm.org/D72959) which makes vtables readonly. This works by replacing the dynamic relocations for function pointers in them with static relocations that represent the offset between the vtable and virtual functions. If a function is externally defined, `dso_local_equivalent` can be used as a generic wrapper for the function to still allow for this static offset calculation to be done. See [RFC](http://lists.llvm.org/pipermail/llvm-dev/2020-August/144469.html) for more details. Differential Revision: https://reviews.llvm.org/D77248
This commit is contained in:
parent
2f3adc54b5
commit
a97f62837f
|
@ -3783,6 +3783,43 @@ long as the original value is reconstituted before the ``indirectbr`` or
|
||||||
Finally, some targets may provide defined semantics when using the value
|
Finally, some targets may provide defined semantics when using the value
|
||||||
as the operand to an inline assembly, but that is target specific.
|
as the operand to an inline assembly, but that is target specific.
|
||||||
|
|
||||||
|
.. _dso_local_equivalent:
|
||||||
|
|
||||||
|
DSO Local Equivalent
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
``dso_local_equivalent @func``
|
||||||
|
|
||||||
|
A '``dso_local_equivalent``' constant represents a function which is
|
||||||
|
functionally equivalent to a given function, but is always defined in the
|
||||||
|
current linkage unit. The resulting pointer has the same type as the underlying
|
||||||
|
function. The resulting pointer is permitted, but not required, to be different
|
||||||
|
from a pointer to the function, and it may have different values in different
|
||||||
|
translation units.
|
||||||
|
|
||||||
|
The target function may not have ``extern_weak`` linkage.
|
||||||
|
|
||||||
|
``dso_local_equivalent`` can be implemented as such:
|
||||||
|
|
||||||
|
- If the function has local linkage, hidden visibility, or is
|
||||||
|
``dso_local``, ``dso_local_equivalent`` can be implemented as simply a pointer
|
||||||
|
to the function.
|
||||||
|
- ``dso_local_equivalent`` can be implemented with a stub that tail-calls the
|
||||||
|
function. Many targets support relocations that resolve at link time to either
|
||||||
|
a function or a stub for it, depending on if the function is defined within the
|
||||||
|
linkage unit; LLVM will use this when available. (This is commonly called a
|
||||||
|
"PLT stub".) On other targets, the stub may need to be emitted explicitly.
|
||||||
|
|
||||||
|
This can be used wherever a ``dso_local`` instance of a function is needed without
|
||||||
|
needing to explicitly make the original function ``dso_local``. An instance where
|
||||||
|
this can be used is for static offset calculations between a function and some other
|
||||||
|
``dso_local`` symbol. This is especially useful for the Relative VTables C++ ABI,
|
||||||
|
where dynamic relocations for function pointers in VTables can be replaced with
|
||||||
|
static relocations for offsets between the VTable and virtual functions which
|
||||||
|
may not be ``dso_local``.
|
||||||
|
|
||||||
|
This is currently only supported for ELF binary formats.
|
||||||
|
|
||||||
.. _constantexprs:
|
.. _constantexprs:
|
||||||
|
|
||||||
Constant Expressions
|
Constant Expressions
|
||||||
|
|
|
@ -25,6 +25,7 @@ template <typename T> class ArrayRef;
|
||||||
class CallBase;
|
class CallBase;
|
||||||
class Constant;
|
class Constant;
|
||||||
class ConstantExpr;
|
class ConstantExpr;
|
||||||
|
class DSOLocalEquivalent;
|
||||||
class DataLayout;
|
class DataLayout;
|
||||||
class Function;
|
class Function;
|
||||||
class GlobalValue;
|
class GlobalValue;
|
||||||
|
@ -34,8 +35,11 @@ class Type;
|
||||||
|
|
||||||
/// If this constant is a constant offset from a global, return the global and
|
/// If this constant is a constant offset from a global, return the global and
|
||||||
/// the constant. Because of constantexprs, this function is recursive.
|
/// the constant. Because of constantexprs, this function is recursive.
|
||||||
|
/// If the global is part of a dso_local_equivalent constant, return it through
|
||||||
|
/// `Equiv` if it is provided.
|
||||||
bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
|
bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
|
||||||
const DataLayout &DL);
|
const DataLayout &DL,
|
||||||
|
DSOLocalEquivalent **DSOEquiv = nullptr);
|
||||||
|
|
||||||
/// ConstantFoldInstruction - Try to constant fold the specified instruction.
|
/// ConstantFoldInstruction - Try to constant fold the specified instruction.
|
||||||
/// If successful, the constant result is returned, if not, null is returned.
|
/// If successful, the constant result is returned, if not, null is returned.
|
||||||
|
|
|
@ -38,7 +38,7 @@ protected:
|
||||||
const TargetMachine *TM = nullptr;
|
const TargetMachine *TM = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TargetLoweringObjectFileELF() = default;
|
TargetLoweringObjectFileELF();
|
||||||
~TargetLoweringObjectFileELF() override = default;
|
~TargetLoweringObjectFileELF() override = default;
|
||||||
|
|
||||||
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
|
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
|
||||||
|
@ -97,6 +97,9 @@ public:
|
||||||
const GlobalValue *RHS,
|
const GlobalValue *RHS,
|
||||||
const TargetMachine &TM) const override;
|
const TargetMachine &TM) const override;
|
||||||
|
|
||||||
|
const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
|
||||||
|
const TargetMachine &TM) const override;
|
||||||
|
|
||||||
MCSection *getSectionForCommandLines() const override;
|
MCSection *getSectionForCommandLines() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -888,6 +888,42 @@ struct OperandTraits<BlockAddress> :
|
||||||
|
|
||||||
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BlockAddress, Value)
|
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BlockAddress, Value)
|
||||||
|
|
||||||
|
/// Wrapper for a function that represents a value that
|
||||||
|
/// functionally represents the original function. This can be a function,
|
||||||
|
/// global alias to a function, or an ifunc.
|
||||||
|
class DSOLocalEquivalent final : public Constant {
|
||||||
|
friend class Constant;
|
||||||
|
|
||||||
|
DSOLocalEquivalent(GlobalValue *GV);
|
||||||
|
|
||||||
|
void *operator new(size_t s) { return User::operator new(s, 1); }
|
||||||
|
|
||||||
|
void destroyConstantImpl();
|
||||||
|
Value *handleOperandChangeImpl(Value *From, Value *To);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Return a DSOLocalEquivalent for the specified global value.
|
||||||
|
static DSOLocalEquivalent *get(GlobalValue *GV);
|
||||||
|
|
||||||
|
/// Transparently provide more efficient getOperand methods.
|
||||||
|
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
|
||||||
|
|
||||||
|
GlobalValue *getGlobalValue() const {
|
||||||
|
return cast<GlobalValue>(Op<0>().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||||
|
static bool classof(const Value *V) {
|
||||||
|
return V->getValueID() == DSOLocalEquivalentVal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct OperandTraits<DSOLocalEquivalent>
|
||||||
|
: public FixedNumOperandTraits<DSOLocalEquivalent, 1> {};
|
||||||
|
|
||||||
|
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(DSOLocalEquivalent, Value)
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
/// A constant value that is initialized with an expression using
|
/// A constant value that is initialized with an expression using
|
||||||
/// other constant values.
|
/// other constant values.
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
#error "Missing macro definition of HANDLE_VALUE*"
|
#error "Missing macro definition of HANDLE_VALUE*"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// If the LLVM_C_API macro is set, then values handled via HANDLE_*_EXCLUDE_LLVM_C_API will not be expanded in areas the HANDLE_* macro is used. If it is not set, then HANDLE_*_EXCLUDE_LLVM_C_API values are handled normally as their HANDLE_* counterparts.
|
||||||
|
#ifndef LLVM_C_API
|
||||||
|
#define LLVM_C_API 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef HANDLE_MEMORY_VALUE
|
#ifndef HANDLE_MEMORY_VALUE
|
||||||
#define HANDLE_MEMORY_VALUE(ValueName) HANDLE_VALUE(ValueName)
|
#define HANDLE_MEMORY_VALUE(ValueName) HANDLE_VALUE(ValueName)
|
||||||
#endif
|
#endif
|
||||||
|
@ -55,6 +60,15 @@
|
||||||
#define HANDLE_CONSTANT_MARKER(MarkerName, ValueName)
|
#define HANDLE_CONSTANT_MARKER(MarkerName, ValueName)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
|
||||||
|
#define HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(ValueName) HANDLE_CONSTANT(ValueName)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LLVM_C_API
|
||||||
|
#undef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
|
||||||
|
#define HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(ValueName)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Having constant first makes the range check for isa<Constant> faster
|
// Having constant first makes the range check for isa<Constant> faster
|
||||||
// and smaller by one operation.
|
// and smaller by one operation.
|
||||||
|
|
||||||
|
@ -65,6 +79,7 @@ HANDLE_GLOBAL_VALUE(GlobalIFunc)
|
||||||
HANDLE_GLOBAL_VALUE(GlobalVariable)
|
HANDLE_GLOBAL_VALUE(GlobalVariable)
|
||||||
HANDLE_CONSTANT(BlockAddress)
|
HANDLE_CONSTANT(BlockAddress)
|
||||||
HANDLE_CONSTANT(ConstantExpr)
|
HANDLE_CONSTANT(ConstantExpr)
|
||||||
|
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(DSOLocalEquivalent)
|
||||||
|
|
||||||
// ConstantAggregate.
|
// ConstantAggregate.
|
||||||
HANDLE_CONSTANT(ConstantArray)
|
HANDLE_CONSTANT(ConstantArray)
|
||||||
|
@ -114,3 +129,5 @@ HANDLE_INSTRUCTION(Instruction)
|
||||||
#undef HANDLE_INLINE_ASM_VALUE
|
#undef HANDLE_INLINE_ASM_VALUE
|
||||||
#undef HANDLE_VALUE
|
#undef HANDLE_VALUE
|
||||||
#undef HANDLE_CONSTANT_MARKER
|
#undef HANDLE_CONSTANT_MARKER
|
||||||
|
#undef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
|
||||||
|
#undef LLVM_C_API
|
||||||
|
|
|
@ -38,6 +38,7 @@ class Module;
|
||||||
class SectionKind;
|
class SectionKind;
|
||||||
class StringRef;
|
class StringRef;
|
||||||
class TargetMachine;
|
class TargetMachine;
|
||||||
|
class DSOLocalEquivalent;
|
||||||
|
|
||||||
class TargetLoweringObjectFile : public MCObjectFileInfo {
|
class TargetLoweringObjectFile : public MCObjectFileInfo {
|
||||||
/// Name-mangler for global names.
|
/// Name-mangler for global names.
|
||||||
|
@ -47,6 +48,7 @@ protected:
|
||||||
bool SupportIndirectSymViaGOTPCRel = false;
|
bool SupportIndirectSymViaGOTPCRel = false;
|
||||||
bool SupportGOTPCRelWithOffset = true;
|
bool SupportGOTPCRelWithOffset = true;
|
||||||
bool SupportDebugThreadLocalLocation = true;
|
bool SupportDebugThreadLocalLocation = true;
|
||||||
|
bool SupportDSOLocalEquivalentLowering = false;
|
||||||
|
|
||||||
/// PersonalityEncoding, LSDAEncoding, TTypeEncoding - Some encoding values
|
/// PersonalityEncoding, LSDAEncoding, TTypeEncoding - Some encoding values
|
||||||
/// for EH.
|
/// for EH.
|
||||||
|
@ -180,6 +182,17 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Target supports a native lowering of a dso_local_equivalent constant
|
||||||
|
/// without needing to replace it with equivalent IR.
|
||||||
|
bool supportDSOLocalEquivalentLowering() const {
|
||||||
|
return SupportDSOLocalEquivalentLowering;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
|
||||||
|
const TargetMachine &TM) const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/// Target supports replacing a data "PC"-relative access to a symbol
|
/// Target supports replacing a data "PC"-relative access to a symbol
|
||||||
/// through another symbol, by accessing the later via a GOT entry instead?
|
/// through another symbol, by accessing the later via a GOT entry instead?
|
||||||
bool supportIndirectSymViaGOTPCRel() const {
|
bool supportIndirectSymViaGOTPCRel() const {
|
||||||
|
|
|
@ -295,7 +295,11 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
|
||||||
/// If this constant is a constant offset from a global, return the global and
|
/// If this constant is a constant offset from a global, return the global and
|
||||||
/// the constant. Because of constantexprs, this function is recursive.
|
/// the constant. Because of constantexprs, this function is recursive.
|
||||||
bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
||||||
APInt &Offset, const DataLayout &DL) {
|
APInt &Offset, const DataLayout &DL,
|
||||||
|
DSOLocalEquivalent **DSOEquiv) {
|
||||||
|
if (DSOEquiv)
|
||||||
|
*DSOEquiv = nullptr;
|
||||||
|
|
||||||
// Trivial case, constant is the global.
|
// Trivial case, constant is the global.
|
||||||
if ((GV = dyn_cast<GlobalValue>(C))) {
|
if ((GV = dyn_cast<GlobalValue>(C))) {
|
||||||
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
|
||||||
|
@ -303,6 +307,15 @@ bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto *FoundDSOEquiv = dyn_cast<DSOLocalEquivalent>(C)) {
|
||||||
|
if (DSOEquiv)
|
||||||
|
*DSOEquiv = FoundDSOEquiv;
|
||||||
|
GV = FoundDSOEquiv->getGlobalValue();
|
||||||
|
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
|
||||||
|
Offset = APInt(BitWidth, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, if this isn't a constant expr, bail out.
|
// Otherwise, if this isn't a constant expr, bail out.
|
||||||
auto *CE = dyn_cast<ConstantExpr>(C);
|
auto *CE = dyn_cast<ConstantExpr>(C);
|
||||||
if (!CE) return false;
|
if (!CE) return false;
|
||||||
|
@ -310,7 +323,8 @@ bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
||||||
// Look through ptr->int and ptr->ptr casts.
|
// Look through ptr->int and ptr->ptr casts.
|
||||||
if (CE->getOpcode() == Instruction::PtrToInt ||
|
if (CE->getOpcode() == Instruction::PtrToInt ||
|
||||||
CE->getOpcode() == Instruction::BitCast)
|
CE->getOpcode() == Instruction::BitCast)
|
||||||
return IsConstantOffsetFromGlobal(CE->getOperand(0), GV, Offset, DL);
|
return IsConstantOffsetFromGlobal(CE->getOperand(0), GV, Offset, DL,
|
||||||
|
DSOEquiv);
|
||||||
|
|
||||||
// i32* getelementptr ([5 x i32]* @a, i32 0, i32 5)
|
// i32* getelementptr ([5 x i32]* @a, i32 0, i32 5)
|
||||||
auto *GEP = dyn_cast<GEPOperator>(CE);
|
auto *GEP = dyn_cast<GEPOperator>(CE);
|
||||||
|
@ -321,7 +335,8 @@ bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
|
||||||
APInt TmpOffset(BitWidth, 0);
|
APInt TmpOffset(BitWidth, 0);
|
||||||
|
|
||||||
// If the base isn't a global+constant, we aren't either.
|
// If the base isn't a global+constant, we aren't either.
|
||||||
if (!IsConstantOffsetFromGlobal(CE->getOperand(0), GV, TmpOffset, DL))
|
if (!IsConstantOffsetFromGlobal(CE->getOperand(0), GV, TmpOffset, DL,
|
||||||
|
DSOEquiv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Otherwise, add any offset that our operands provide.
|
// Otherwise, add any offset that our operands provide.
|
||||||
|
|
|
@ -724,6 +724,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||||
KEYWORD(vscale);
|
KEYWORD(vscale);
|
||||||
KEYWORD(x);
|
KEYWORD(x);
|
||||||
KEYWORD(blockaddress);
|
KEYWORD(blockaddress);
|
||||||
|
KEYWORD(dso_local_equivalent);
|
||||||
|
|
||||||
// Metadata types.
|
// Metadata types.
|
||||||
KEYWORD(distinct);
|
KEYWORD(distinct);
|
||||||
|
|
|
@ -3491,6 +3491,39 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case lltok::kw_dso_local_equivalent: {
|
||||||
|
// ValID ::= 'dso_local_equivalent' @foo
|
||||||
|
Lex.Lex();
|
||||||
|
|
||||||
|
ValID Fn;
|
||||||
|
|
||||||
|
if (parseValID(Fn))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName)
|
||||||
|
return error(Fn.Loc,
|
||||||
|
"expected global value name in dso_local_equivalent");
|
||||||
|
|
||||||
|
// Try to find the function (but skip it if it's forward-referenced).
|
||||||
|
GlobalValue *GV = nullptr;
|
||||||
|
if (Fn.Kind == ValID::t_GlobalID) {
|
||||||
|
if (Fn.UIntVal < NumberedVals.size())
|
||||||
|
GV = NumberedVals[Fn.UIntVal];
|
||||||
|
} else if (!ForwardRefVals.count(Fn.StrVal)) {
|
||||||
|
GV = M->getNamedValue(Fn.StrVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(GV && "Could not find a corresponding global variable");
|
||||||
|
|
||||||
|
if (!GV->getValueType()->isFunctionTy())
|
||||||
|
return error(Fn.Loc, "expected a function, alias to function, or ifunc "
|
||||||
|
"in dso_local_equivalent");
|
||||||
|
|
||||||
|
ID.ConstantVal = DSOLocalEquivalent::get(GV);
|
||||||
|
ID.Kind = ValID::t_Constant;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
case lltok::kw_trunc:
|
case lltok::kw_trunc:
|
||||||
case lltok::kw_zext:
|
case lltok::kw_zext:
|
||||||
case lltok::kw_sext:
|
case lltok::kw_sext:
|
||||||
|
|
|
@ -360,6 +360,7 @@ enum Kind {
|
||||||
kw_extractvalue,
|
kw_extractvalue,
|
||||||
kw_insertvalue,
|
kw_insertvalue,
|
||||||
kw_blockaddress,
|
kw_blockaddress,
|
||||||
|
kw_dso_local_equivalent,
|
||||||
|
|
||||||
kw_freeze,
|
kw_freeze,
|
||||||
|
|
||||||
|
|
|
@ -2341,6 +2341,9 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
|
||||||
if (const BlockAddress *BA = dyn_cast<BlockAddress>(CV))
|
if (const BlockAddress *BA = dyn_cast<BlockAddress>(CV))
|
||||||
return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx);
|
return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx);
|
||||||
|
|
||||||
|
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV))
|
||||||
|
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM);
|
||||||
|
|
||||||
const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV);
|
const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV);
|
||||||
if (!CE) {
|
if (!CE) {
|
||||||
llvm_unreachable("Unknown constant value to lower!");
|
llvm_unreachable("Unknown constant value to lower!");
|
||||||
|
@ -2437,18 +2440,25 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
|
||||||
case Instruction::Sub: {
|
case Instruction::Sub: {
|
||||||
GlobalValue *LHSGV;
|
GlobalValue *LHSGV;
|
||||||
APInt LHSOffset;
|
APInt LHSOffset;
|
||||||
|
DSOLocalEquivalent *DSOEquiv;
|
||||||
if (IsConstantOffsetFromGlobal(CE->getOperand(0), LHSGV, LHSOffset,
|
if (IsConstantOffsetFromGlobal(CE->getOperand(0), LHSGV, LHSOffset,
|
||||||
getDataLayout())) {
|
getDataLayout(), &DSOEquiv)) {
|
||||||
GlobalValue *RHSGV;
|
GlobalValue *RHSGV;
|
||||||
APInt RHSOffset;
|
APInt RHSOffset;
|
||||||
if (IsConstantOffsetFromGlobal(CE->getOperand(1), RHSGV, RHSOffset,
|
if (IsConstantOffsetFromGlobal(CE->getOperand(1), RHSGV, RHSOffset,
|
||||||
getDataLayout())) {
|
getDataLayout())) {
|
||||||
const MCExpr *RelocExpr =
|
const MCExpr *RelocExpr =
|
||||||
getObjFileLowering().lowerRelativeReference(LHSGV, RHSGV, TM);
|
getObjFileLowering().lowerRelativeReference(LHSGV, RHSGV, TM);
|
||||||
if (!RelocExpr)
|
if (!RelocExpr) {
|
||||||
|
const MCExpr *LHSExpr =
|
||||||
|
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx);
|
||||||
|
if (DSOEquiv &&
|
||||||
|
getObjFileLowering().supportDSOLocalEquivalentLowering())
|
||||||
|
LHSExpr =
|
||||||
|
getObjFileLowering().lowerDSOLocalEquivalent(DSOEquiv, TM);
|
||||||
RelocExpr = MCBinaryExpr::createSub(
|
RelocExpr = MCBinaryExpr::createSub(
|
||||||
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx),
|
LHSExpr, MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
|
||||||
MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
|
}
|
||||||
int64_t Addend = (LHSOffset - RHSOffset).getSExtValue();
|
int64_t Addend = (LHSOffset - RHSOffset).getSExtValue();
|
||||||
if (Addend != 0)
|
if (Addend != 0)
|
||||||
RelocExpr = MCBinaryExpr::createAdd(
|
RelocExpr = MCBinaryExpr::createAdd(
|
||||||
|
|
|
@ -1514,6 +1514,9 @@ SDValue SelectionDAGBuilder::getValueImpl(const Value *V) {
|
||||||
if (const BlockAddress *BA = dyn_cast<BlockAddress>(C))
|
if (const BlockAddress *BA = dyn_cast<BlockAddress>(C))
|
||||||
return DAG.getBlockAddress(BA, VT);
|
return DAG.getBlockAddress(BA, VT);
|
||||||
|
|
||||||
|
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(C))
|
||||||
|
return getValue(Equiv->getGlobalValue());
|
||||||
|
|
||||||
VectorType *VecTy = cast<VectorType>(V->getType());
|
VectorType *VecTy = cast<VectorType>(V->getType());
|
||||||
|
|
||||||
// Now that we know the number and type of the elements, get that number of
|
// Now that we know the number and type of the elements, get that number of
|
||||||
|
|
|
@ -105,6 +105,11 @@ static void GetObjCImageInfo(Module &M, unsigned &Version, unsigned &Flags,
|
||||||
// ELF
|
// ELF
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
TargetLoweringObjectFileELF::TargetLoweringObjectFileELF()
|
||||||
|
: TargetLoweringObjectFile() {
|
||||||
|
SupportDSOLocalEquivalentLowering = true;
|
||||||
|
}
|
||||||
|
|
||||||
void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx,
|
void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx,
|
||||||
const TargetMachine &TgtM) {
|
const TargetMachine &TgtM) {
|
||||||
TargetLoweringObjectFile::Initialize(Ctx, TgtM);
|
TargetLoweringObjectFile::Initialize(Ctx, TgtM);
|
||||||
|
@ -1007,6 +1012,20 @@ const MCExpr *TargetLoweringObjectFileELF::lowerRelativeReference(
|
||||||
MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext());
|
MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MCExpr *TargetLoweringObjectFileELF::lowerDSOLocalEquivalent(
|
||||||
|
const DSOLocalEquivalent *Equiv, const TargetMachine &TM) const {
|
||||||
|
assert(supportDSOLocalEquivalentLowering());
|
||||||
|
|
||||||
|
const auto *GV = Equiv->getGlobalValue();
|
||||||
|
|
||||||
|
// A PLT entry is not needed for dso_local globals.
|
||||||
|
if (GV->isDSOLocal() || GV->isImplicitDSOLocal())
|
||||||
|
return MCSymbolRefExpr::create(TM.getSymbol(GV), getContext());
|
||||||
|
|
||||||
|
return MCSymbolRefExpr::create(TM.getSymbol(GV), PLTRelativeVariantKind,
|
||||||
|
getContext());
|
||||||
|
}
|
||||||
|
|
||||||
MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const {
|
MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const {
|
||||||
// Use ".GCC.command.line" since this feature is to support clang's
|
// Use ".GCC.command.line" since this feature is to support clang's
|
||||||
// -frecord-gcc-switches which in turn attempts to mimic GCC's switch of the
|
// -frecord-gcc-switches which in turn attempts to mimic GCC's switch of the
|
||||||
|
|
|
@ -1455,6 +1455,13 @@ static void WriteConstantInternal(raw_ostream &Out, const Constant *CV,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV)) {
|
||||||
|
Out << "dso_local_equivalent ";
|
||||||
|
WriteAsOperandInternal(Out, Equiv->getGlobalValue(), &TypePrinter, Machine,
|
||||||
|
Context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) {
|
if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) {
|
||||||
Type *ETy = CA->getType()->getElementType();
|
Type *ETy = CA->getType()->getElementType();
|
||||||
Out << '[';
|
Out << '[';
|
||||||
|
|
|
@ -509,6 +509,9 @@ void llvm::deleteConstant(Constant *C) {
|
||||||
case Constant::BlockAddressVal:
|
case Constant::BlockAddressVal:
|
||||||
delete static_cast<BlockAddress *>(C);
|
delete static_cast<BlockAddress *>(C);
|
||||||
break;
|
break;
|
||||||
|
case Constant::DSOLocalEquivalentVal:
|
||||||
|
delete static_cast<DSOLocalEquivalent *>(C);
|
||||||
|
break;
|
||||||
case Constant::UndefValueVal:
|
case Constant::UndefValueVal:
|
||||||
delete static_cast<UndefValue *>(C);
|
delete static_cast<UndefValue *>(C);
|
||||||
break;
|
break;
|
||||||
|
@ -654,10 +657,17 @@ bool Constant::needsRelocation() const {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Relative pointers do not need to be dynamically relocated.
|
// Relative pointers do not need to be dynamically relocated.
|
||||||
if (auto *LHSGV = dyn_cast<GlobalValue>(LHSOp0->stripPointerCasts()))
|
if (auto *RHSGV =
|
||||||
if (auto *RHSGV = dyn_cast<GlobalValue>(RHSOp0->stripPointerCasts()))
|
dyn_cast<GlobalValue>(RHSOp0->stripInBoundsConstantOffsets())) {
|
||||||
|
auto *LHS = LHSOp0->stripInBoundsConstantOffsets();
|
||||||
|
if (auto *LHSGV = dyn_cast<GlobalValue>(LHS)) {
|
||||||
if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal())
|
if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal())
|
||||||
return false;
|
return false;
|
||||||
|
} else if (isa<DSOLocalEquivalent>(LHS)) {
|
||||||
|
if (RHSGV->isDSOLocal())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1763,6 +1773,54 @@ Value *BlockAddress::handleOperandChangeImpl(Value *From, Value *To) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DSOLocalEquivalent *DSOLocalEquivalent::get(GlobalValue *GV) {
|
||||||
|
DSOLocalEquivalent *&Equiv = GV->getContext().pImpl->DSOLocalEquivalents[GV];
|
||||||
|
if (!Equiv)
|
||||||
|
Equiv = new DSOLocalEquivalent(GV);
|
||||||
|
|
||||||
|
assert(Equiv->getGlobalValue() == GV &&
|
||||||
|
"DSOLocalFunction does not match the expected global value");
|
||||||
|
return Equiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
DSOLocalEquivalent::DSOLocalEquivalent(GlobalValue *GV)
|
||||||
|
: Constant(GV->getType(), Value::DSOLocalEquivalentVal, &Op<0>(), 1) {
|
||||||
|
setOperand(0, GV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the constant from the constant table.
|
||||||
|
void DSOLocalEquivalent::destroyConstantImpl() {
|
||||||
|
const GlobalValue *GV = getGlobalValue();
|
||||||
|
GV->getContext().pImpl->DSOLocalEquivalents.erase(GV);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *DSOLocalEquivalent::handleOperandChangeImpl(Value *From, Value *To) {
|
||||||
|
assert(From == getGlobalValue() && "Changing value does not match operand.");
|
||||||
|
assert(To->getType() == getType() && "Mismatched types");
|
||||||
|
assert(isa<Constant>(To) && "Can only replace the operands with a constant");
|
||||||
|
|
||||||
|
// The replacement is with another global value.
|
||||||
|
if (const auto *ToObj = dyn_cast<GlobalValue>(To)) {
|
||||||
|
DSOLocalEquivalent *&NewEquiv =
|
||||||
|
getContext().pImpl->DSOLocalEquivalents[ToObj];
|
||||||
|
if (NewEquiv)
|
||||||
|
return NewEquiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The replacement could be a bitcast or an alias to another function. We can
|
||||||
|
// replace it with a bitcast to the dso_local_equivalent of that function.
|
||||||
|
auto *Func = cast<Function>(To->stripPointerCastsAndAliases());
|
||||||
|
DSOLocalEquivalent *&NewEquiv = getContext().pImpl->DSOLocalEquivalents[Func];
|
||||||
|
if (NewEquiv)
|
||||||
|
return llvm::ConstantExpr::getBitCast(NewEquiv, getType());
|
||||||
|
|
||||||
|
// Replace this with the new one.
|
||||||
|
getContext().pImpl->DSOLocalEquivalents.erase(getGlobalValue());
|
||||||
|
NewEquiv = this;
|
||||||
|
setOperand(0, Func);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
//---- ConstantExpr::get() implementations.
|
//---- ConstantExpr::get() implementations.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
|
@ -826,6 +826,7 @@ LLVMTypeRef LLVMTypeOf(LLVMValueRef Val) {
|
||||||
|
|
||||||
LLVMValueKind LLVMGetValueKind(LLVMValueRef Val) {
|
LLVMValueKind LLVMGetValueKind(LLVMValueRef Val) {
|
||||||
switch(unwrap(Val)->getValueID()) {
|
switch(unwrap(Val)->getValueID()) {
|
||||||
|
#define LLVM_C_API 1
|
||||||
#define HANDLE_VALUE(Name) \
|
#define HANDLE_VALUE(Name) \
|
||||||
case Value::Name##Val: \
|
case Value::Name##Val: \
|
||||||
return LLVM##Name##ValueKind;
|
return LLVM##Name##ValueKind;
|
||||||
|
|
|
@ -1380,6 +1380,9 @@ public:
|
||||||
|
|
||||||
DenseMap<std::pair<const Function *, const BasicBlock *>, BlockAddress *>
|
DenseMap<std::pair<const Function *, const BasicBlock *>, BlockAddress *>
|
||||||
BlockAddresses;
|
BlockAddresses;
|
||||||
|
|
||||||
|
DenseMap<const GlobalValue *, DSOLocalEquivalent *> DSOLocalEquivalents;
|
||||||
|
|
||||||
ConstantUniqueMap<ConstantExpr> ExprConstants;
|
ConstantUniqueMap<ConstantExpr> ExprConstants;
|
||||||
|
|
||||||
ConstantUniqueMap<InlineAsm> InlineAsms;
|
ConstantUniqueMap<InlineAsm> InlineAsms;
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
; RUN: llc -mtriple=x86_64-linux-gnu -relocation-model=pic -data-sections -o - %s --asm-verbose=0 | FileCheck %s -check-prefixes=CHECK
|
||||||
|
|
||||||
|
; Just ensure that we can write to an object file without error.
|
||||||
|
; RUN: llc -filetype=obj -mtriple=x86_64-linux-gnu -relocation-model=pic -data-sections -o /dev/null %s
|
||||||
|
|
||||||
|
declare void @extern_func()
|
||||||
|
|
||||||
|
; CHECK: call_extern_func:
|
||||||
|
; CHECK: callq extern_func@PLT
|
||||||
|
define void @call_extern_func() {
|
||||||
|
call void dso_local_equivalent @extern_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare hidden void @hidden_func()
|
||||||
|
declare protected void @protected_func()
|
||||||
|
declare dso_local void @dso_local_func()
|
||||||
|
define internal void @internal_func() {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
define private void @private_func() {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_hidden_func:
|
||||||
|
; CHECK: callq hidden_func{{$}}
|
||||||
|
define void @call_hidden_func() {
|
||||||
|
call void dso_local_equivalent @hidden_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_protected_func:
|
||||||
|
; CHECK: callq protected_func{{$}}
|
||||||
|
define void @call_protected_func() {
|
||||||
|
call void dso_local_equivalent @protected_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_dso_local_func:
|
||||||
|
; CHECK: callq dso_local_func{{$}}
|
||||||
|
define void @call_dso_local_func() {
|
||||||
|
call void dso_local_equivalent @dso_local_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_internal_func:
|
||||||
|
; CHECK: callq internal_func{{$}}
|
||||||
|
define void @call_internal_func() {
|
||||||
|
call void dso_local_equivalent @internal_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @aliasee_func() {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
@alias_func = alias void (), void ()* @aliasee_func
|
||||||
|
@dso_local_alias_func = dso_local alias void (), void ()* @aliasee_func
|
||||||
|
|
||||||
|
; CHECK: call_alias_func:
|
||||||
|
; CHECK: callq alias_func@PLT
|
||||||
|
define void @call_alias_func() {
|
||||||
|
call void dso_local_equivalent @alias_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_dso_local_alias_func:
|
||||||
|
; CHECK: callq .Ldso_local_alias_func$local{{$}}
|
||||||
|
define void @call_dso_local_alias_func() {
|
||||||
|
call void dso_local_equivalent @dso_local_alias_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
@ifunc_func = ifunc void (), i64 ()* @resolver
|
||||||
|
@dso_local_ifunc_func = dso_local ifunc void (), i64 ()* @resolver
|
||||||
|
|
||||||
|
define internal i64 @resolver() {
|
||||||
|
entry:
|
||||||
|
ret i64 0
|
||||||
|
}
|
||||||
|
|
||||||
|
; If an ifunc is not dso_local already, then we should still emit a stub for it
|
||||||
|
; to ensure it will be dso_local.
|
||||||
|
; CHECK: call_ifunc_func:
|
||||||
|
; CHECK: callq ifunc_func@PLT
|
||||||
|
define void @call_ifunc_func() {
|
||||||
|
call void dso_local_equivalent @ifunc_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: call_dso_local_ifunc_func:
|
||||||
|
; CHECK: callq dso_local_ifunc_func{{$}}
|
||||||
|
define void @call_dso_local_ifunc_func() {
|
||||||
|
call void dso_local_equivalent @dso_local_ifunc_func()
|
||||||
|
ret void
|
||||||
|
}
|
|
@ -19,3 +19,27 @@ target triple = "x86_64-unknown-linux-gnu"
|
||||||
; CHECK: relro2:
|
; CHECK: relro2:
|
||||||
; CHECK: .long hidden-relro2
|
; CHECK: .long hidden-relro2
|
||||||
@relro2 = constant i32 trunc (i64 sub (i64 ptrtoint (i8* @hidden to i64), i64 ptrtoint (i32* @relro2 to i64)) to i32)
|
@relro2 = constant i32 trunc (i64 sub (i64 ptrtoint (i8* @hidden to i64), i64 ptrtoint (i32* @relro2 to i64)) to i32)
|
||||||
|
|
||||||
|
; CHECK: .section .rodata.cst8
|
||||||
|
; CHECK-NEXT: .globl obj
|
||||||
|
; CHECK: obj:
|
||||||
|
; CHECK: .long 0
|
||||||
|
; CHECK: .long (hidden_func-obj)-4
|
||||||
|
|
||||||
|
declare hidden void @hidden_func()
|
||||||
|
|
||||||
|
; Ensure that inbound GEPs with constant offsets are also resolved.
|
||||||
|
@obj = dso_local unnamed_addr constant { { i32, i32 } } {
|
||||||
|
{ i32, i32 } {
|
||||||
|
i32 0,
|
||||||
|
i32 trunc (i64 sub (i64 ptrtoint (void ()* dso_local_equivalent @hidden_func to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i32, i32 } }, { { i32, i32 } }* @obj, i32 0, i32 0, i32 1) to i64)) to i32)
|
||||||
|
} }, align 4
|
||||||
|
|
||||||
|
; CHECK: .section .rodata.rodata2
|
||||||
|
; CHECK-NEXT: .globl rodata2
|
||||||
|
; CHECK: rodata2:
|
||||||
|
; CHECK: .long extern_func@PLT-rodata2
|
||||||
|
|
||||||
|
declare void @extern_func()
|
||||||
|
|
||||||
|
@rodata2 = dso_local constant i32 trunc (i64 sub (i64 ptrtoint (void ()* dso_local_equivalent @extern_func to i64), i64 ptrtoint (i32* @rodata2 to i64)) to i32)
|
||||||
|
|
Loading…
Reference in New Issue