Skip to content

Commit a97f628

Browse files
committed
[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
1 parent 2f3adc5 commit a97f628

19 files changed

+395
-11
lines changed

llvm/docs/LangRef.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3783,6 +3783,43 @@ long as the original value is reconstituted before the ``indirectbr`` or
37833783
Finally, some targets may provide defined semantics when using the value
37843784
as the operand to an inline assembly, but that is target specific.
37853785

3786+
.. _dso_local_equivalent:
3787+
3788+
DSO Local Equivalent
3789+
--------------------
3790+
3791+
``dso_local_equivalent @func``
3792+
3793+
A '``dso_local_equivalent``' constant represents a function which is
3794+
functionally equivalent to a given function, but is always defined in the
3795+
current linkage unit. The resulting pointer has the same type as the underlying
3796+
function. The resulting pointer is permitted, but not required, to be different
3797+
from a pointer to the function, and it may have different values in different
3798+
translation units.
3799+
3800+
The target function may not have ``extern_weak`` linkage.
3801+
3802+
``dso_local_equivalent`` can be implemented as such:
3803+
3804+
- If the function has local linkage, hidden visibility, or is
3805+
``dso_local``, ``dso_local_equivalent`` can be implemented as simply a pointer
3806+
to the function.
3807+
- ``dso_local_equivalent`` can be implemented with a stub that tail-calls the
3808+
function. Many targets support relocations that resolve at link time to either
3809+
a function or a stub for it, depending on if the function is defined within the
3810+
linkage unit; LLVM will use this when available. (This is commonly called a
3811+
"PLT stub".) On other targets, the stub may need to be emitted explicitly.
3812+
3813+
This can be used wherever a ``dso_local`` instance of a function is needed without
3814+
needing to explicitly make the original function ``dso_local``. An instance where
3815+
this can be used is for static offset calculations between a function and some other
3816+
``dso_local`` symbol. This is especially useful for the Relative VTables C++ ABI,
3817+
where dynamic relocations for function pointers in VTables can be replaced with
3818+
static relocations for offsets between the VTable and virtual functions which
3819+
may not be ``dso_local``.
3820+
3821+
This is currently only supported for ELF binary formats.
3822+
37863823
.. _constantexprs:
37873824

37883825
Constant Expressions

llvm/include/llvm/Analysis/ConstantFolding.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ template <typename T> class ArrayRef;
2525
class CallBase;
2626
class Constant;
2727
class ConstantExpr;
28+
class DSOLocalEquivalent;
2829
class DataLayout;
2930
class Function;
3031
class GlobalValue;
@@ -34,8 +35,11 @@ class Type;
3435

3536
/// If this constant is a constant offset from a global, return the global and
3637
/// the constant. Because of constantexprs, this function is recursive.
38+
/// If the global is part of a dso_local_equivalent constant, return it through
39+
/// `Equiv` if it is provided.
3740
bool IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV, APInt &Offset,
38-
const DataLayout &DL);
41+
const DataLayout &DL,
42+
DSOLocalEquivalent **DSOEquiv = nullptr);
3943

4044
/// ConstantFoldInstruction - Try to constant fold the specified instruction.
4145
/// If successful, the constant result is returned, if not, null is returned.

llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
3838
const TargetMachine *TM = nullptr;
3939

4040
public:
41-
TargetLoweringObjectFileELF() = default;
41+
TargetLoweringObjectFileELF();
4242
~TargetLoweringObjectFileELF() override = default;
4343

4444
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
@@ -97,6 +97,9 @@ class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
9797
const GlobalValue *RHS,
9898
const TargetMachine &TM) const override;
9999

100+
const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
101+
const TargetMachine &TM) const override;
102+
100103
MCSection *getSectionForCommandLines() const override;
101104
};
102105

llvm/include/llvm/IR/Constants.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,42 @@ struct OperandTraits<BlockAddress> :
888888

889889
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BlockAddress, Value)
890890

891+
/// Wrapper for a function that represents a value that
892+
/// functionally represents the original function. This can be a function,
893+
/// global alias to a function, or an ifunc.
894+
class DSOLocalEquivalent final : public Constant {
895+
friend class Constant;
896+
897+
DSOLocalEquivalent(GlobalValue *GV);
898+
899+
void *operator new(size_t s) { return User::operator new(s, 1); }
900+
901+
void destroyConstantImpl();
902+
Value *handleOperandChangeImpl(Value *From, Value *To);
903+
904+
public:
905+
/// Return a DSOLocalEquivalent for the specified global value.
906+
static DSOLocalEquivalent *get(GlobalValue *GV);
907+
908+
/// Transparently provide more efficient getOperand methods.
909+
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value);
910+
911+
GlobalValue *getGlobalValue() const {
912+
return cast<GlobalValue>(Op<0>().get());
913+
}
914+
915+
/// Methods for support type inquiry through isa, cast, and dyn_cast:
916+
static bool classof(const Value *V) {
917+
return V->getValueID() == DSOLocalEquivalentVal;
918+
}
919+
};
920+
921+
template <>
922+
struct OperandTraits<DSOLocalEquivalent>
923+
: public FixedNumOperandTraits<DSOLocalEquivalent, 1> {};
924+
925+
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(DSOLocalEquivalent, Value)
926+
891927
//===----------------------------------------------------------------------===//
892928
/// A constant value that is initialized with an expression using
893929
/// other constant values.

llvm/include/llvm/IR/Value.def

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#error "Missing macro definition of HANDLE_VALUE*"
2424
#endif
2525

26+
// 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.
27+
#ifndef LLVM_C_API
28+
#define LLVM_C_API 0
29+
#endif
30+
2631
#ifndef HANDLE_MEMORY_VALUE
2732
#define HANDLE_MEMORY_VALUE(ValueName) HANDLE_VALUE(ValueName)
2833
#endif
@@ -55,6 +60,15 @@
5560
#define HANDLE_CONSTANT_MARKER(MarkerName, ValueName)
5661
#endif
5762

63+
#ifndef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
64+
#define HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(ValueName) HANDLE_CONSTANT(ValueName)
65+
#endif
66+
67+
#if LLVM_C_API
68+
#undef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
69+
#define HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(ValueName)
70+
#endif
71+
5872
// Having constant first makes the range check for isa<Constant> faster
5973
// and smaller by one operation.
6074

@@ -65,6 +79,7 @@ HANDLE_GLOBAL_VALUE(GlobalIFunc)
6579
HANDLE_GLOBAL_VALUE(GlobalVariable)
6680
HANDLE_CONSTANT(BlockAddress)
6781
HANDLE_CONSTANT(ConstantExpr)
82+
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(DSOLocalEquivalent)
6883

6984
// ConstantAggregate.
7085
HANDLE_CONSTANT(ConstantArray)
@@ -114,3 +129,5 @@ HANDLE_INSTRUCTION(Instruction)
114129
#undef HANDLE_INLINE_ASM_VALUE
115130
#undef HANDLE_VALUE
116131
#undef HANDLE_CONSTANT_MARKER
132+
#undef HANDLE_CONSTANT_EXCLUDE_LLVM_C_API
133+
#undef LLVM_C_API

llvm/include/llvm/Target/TargetLoweringObjectFile.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class Module;
3838
class SectionKind;
3939
class StringRef;
4040
class TargetMachine;
41+
class DSOLocalEquivalent;
4142

4243
class TargetLoweringObjectFile : public MCObjectFileInfo {
4344
/// Name-mangler for global names.
@@ -47,6 +48,7 @@ class TargetLoweringObjectFile : public MCObjectFileInfo {
4748
bool SupportIndirectSymViaGOTPCRel = false;
4849
bool SupportGOTPCRelWithOffset = true;
4950
bool SupportDebugThreadLocalLocation = true;
51+
bool SupportDSOLocalEquivalentLowering = false;
5052

5153
/// PersonalityEncoding, LSDAEncoding, TTypeEncoding - Some encoding values
5254
/// for EH.
@@ -180,6 +182,17 @@ class TargetLoweringObjectFile : public MCObjectFileInfo {
180182
return nullptr;
181183
}
182184

185+
/// Target supports a native lowering of a dso_local_equivalent constant
186+
/// without needing to replace it with equivalent IR.
187+
bool supportDSOLocalEquivalentLowering() const {
188+
return SupportDSOLocalEquivalentLowering;
189+
}
190+
191+
virtual const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv,
192+
const TargetMachine &TM) const {
193+
return nullptr;
194+
}
195+
183196
/// Target supports replacing a data "PC"-relative access to a symbol
184197
/// through another symbol, by accessing the later via a GOT entry instead?
185198
bool supportIndirectSymViaGOTPCRel() const {

llvm/lib/Analysis/ConstantFolding.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,22 +295,36 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
295295
/// If this constant is a constant offset from a global, return the global and
296296
/// the constant. Because of constantexprs, this function is recursive.
297297
bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
298-
APInt &Offset, const DataLayout &DL) {
298+
APInt &Offset, const DataLayout &DL,
299+
DSOLocalEquivalent **DSOEquiv) {
300+
if (DSOEquiv)
301+
*DSOEquiv = nullptr;
302+
299303
// Trivial case, constant is the global.
300304
if ((GV = dyn_cast<GlobalValue>(C))) {
301305
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
302306
Offset = APInt(BitWidth, 0);
303307
return true;
304308
}
305309

310+
if (auto *FoundDSOEquiv = dyn_cast<DSOLocalEquivalent>(C)) {
311+
if (DSOEquiv)
312+
*DSOEquiv = FoundDSOEquiv;
313+
GV = FoundDSOEquiv->getGlobalValue();
314+
unsigned BitWidth = DL.getIndexTypeSizeInBits(GV->getType());
315+
Offset = APInt(BitWidth, 0);
316+
return true;
317+
}
318+
306319
// Otherwise, if this isn't a constant expr, bail out.
307320
auto *CE = dyn_cast<ConstantExpr>(C);
308321
if (!CE) return false;
309322

310323
// Look through ptr->int and ptr->ptr casts.
311324
if (CE->getOpcode() == Instruction::PtrToInt ||
312325
CE->getOpcode() == Instruction::BitCast)
313-
return IsConstantOffsetFromGlobal(CE->getOperand(0), GV, Offset, DL);
326+
return IsConstantOffsetFromGlobal(CE->getOperand(0), GV, Offset, DL,
327+
DSOEquiv);
314328

315329
// i32* getelementptr ([5 x i32]* @a, i32 0, i32 5)
316330
auto *GEP = dyn_cast<GEPOperator>(CE);
@@ -321,7 +335,8 @@ bool llvm::IsConstantOffsetFromGlobal(Constant *C, GlobalValue *&GV,
321335
APInt TmpOffset(BitWidth, 0);
322336

323337
// If the base isn't a global+constant, we aren't either.
324-
if (!IsConstantOffsetFromGlobal(CE->getOperand(0), GV, TmpOffset, DL))
338+
if (!IsConstantOffsetFromGlobal(CE->getOperand(0), GV, TmpOffset, DL,
339+
DSOEquiv))
325340
return false;
326341

327342
// Otherwise, add any offset that our operands provide.

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ lltok::Kind LLLexer::LexIdentifier() {
724724
KEYWORD(vscale);
725725
KEYWORD(x);
726726
KEYWORD(blockaddress);
727+
KEYWORD(dso_local_equivalent);
727728

728729
// Metadata types.
729730
KEYWORD(distinct);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,6 +3491,39 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS) {
34913491
return false;
34923492
}
34933493

3494+
case lltok::kw_dso_local_equivalent: {
3495+
// ValID ::= 'dso_local_equivalent' @foo
3496+
Lex.Lex();
3497+
3498+
ValID Fn;
3499+
3500+
if (parseValID(Fn))
3501+
return true;
3502+
3503+
if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName)
3504+
return error(Fn.Loc,
3505+
"expected global value name in dso_local_equivalent");
3506+
3507+
// Try to find the function (but skip it if it's forward-referenced).
3508+
GlobalValue *GV = nullptr;
3509+
if (Fn.Kind == ValID::t_GlobalID) {
3510+
if (Fn.UIntVal < NumberedVals.size())
3511+
GV = NumberedVals[Fn.UIntVal];
3512+
} else if (!ForwardRefVals.count(Fn.StrVal)) {
3513+
GV = M->getNamedValue(Fn.StrVal);
3514+
}
3515+
3516+
assert(GV && "Could not find a corresponding global variable");
3517+
3518+
if (!GV->getValueType()->isFunctionTy())
3519+
return error(Fn.Loc, "expected a function, alias to function, or ifunc "
3520+
"in dso_local_equivalent");
3521+
3522+
ID.ConstantVal = DSOLocalEquivalent::get(GV);
3523+
ID.Kind = ValID::t_Constant;
3524+
return false;
3525+
}
3526+
34943527
case lltok::kw_trunc:
34953528
case lltok::kw_zext:
34963529
case lltok::kw_sext:

llvm/lib/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ enum Kind {
360360
kw_extractvalue,
361361
kw_insertvalue,
362362
kw_blockaddress,
363+
kw_dso_local_equivalent,
363364

364365
kw_freeze,
365366

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,9 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
23412341
if (const BlockAddress *BA = dyn_cast<BlockAddress>(CV))
23422342
return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx);
23432343

2344+
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV))
2345+
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM);
2346+
23442347
const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV);
23452348
if (!CE) {
23462349
llvm_unreachable("Unknown constant value to lower!");
@@ -2437,18 +2440,25 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
24372440
case Instruction::Sub: {
24382441
GlobalValue *LHSGV;
24392442
APInt LHSOffset;
2443+
DSOLocalEquivalent *DSOEquiv;
24402444
if (IsConstantOffsetFromGlobal(CE->getOperand(0), LHSGV, LHSOffset,
2441-
getDataLayout())) {
2445+
getDataLayout(), &DSOEquiv)) {
24422446
GlobalValue *RHSGV;
24432447
APInt RHSOffset;
24442448
if (IsConstantOffsetFromGlobal(CE->getOperand(1), RHSGV, RHSOffset,
24452449
getDataLayout())) {
24462450
const MCExpr *RelocExpr =
24472451
getObjFileLowering().lowerRelativeReference(LHSGV, RHSGV, TM);
2448-
if (!RelocExpr)
2452+
if (!RelocExpr) {
2453+
const MCExpr *LHSExpr =
2454+
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx);
2455+
if (DSOEquiv &&
2456+
getObjFileLowering().supportDSOLocalEquivalentLowering())
2457+
LHSExpr =
2458+
getObjFileLowering().lowerDSOLocalEquivalent(DSOEquiv, TM);
24492459
RelocExpr = MCBinaryExpr::createSub(
2450-
MCSymbolRefExpr::create(getSymbol(LHSGV), Ctx),
2451-
MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
2460+
LHSExpr, MCSymbolRefExpr::create(getSymbol(RHSGV), Ctx), Ctx);
2461+
}
24522462
int64_t Addend = (LHSOffset - RHSOffset).getSExtValue();
24532463
if (Addend != 0)
24542464
RelocExpr = MCBinaryExpr::createAdd(

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,9 @@ SDValue SelectionDAGBuilder::getValueImpl(const Value *V) {
15141514
if (const BlockAddress *BA = dyn_cast<BlockAddress>(C))
15151515
return DAG.getBlockAddress(BA, VT);
15161516

1517+
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(C))
1518+
return getValue(Equiv->getGlobalValue());
1519+
15171520
VectorType *VecTy = cast<VectorType>(V->getType());
15181521

15191522
// Now that we know the number and type of the elements, get that number of

llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ static void GetObjCImageInfo(Module &M, unsigned &Version, unsigned &Flags,
105105
// ELF
106106
//===----------------------------------------------------------------------===//
107107

108+
TargetLoweringObjectFileELF::TargetLoweringObjectFileELF()
109+
: TargetLoweringObjectFile() {
110+
SupportDSOLocalEquivalentLowering = true;
111+
}
112+
108113
void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx,
109114
const TargetMachine &TgtM) {
110115
TargetLoweringObjectFile::Initialize(Ctx, TgtM);
@@ -1007,6 +1012,20 @@ const MCExpr *TargetLoweringObjectFileELF::lowerRelativeReference(
10071012
MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext());
10081013
}
10091014

1015+
const MCExpr *TargetLoweringObjectFileELF::lowerDSOLocalEquivalent(
1016+
const DSOLocalEquivalent *Equiv, const TargetMachine &TM) const {
1017+
assert(supportDSOLocalEquivalentLowering());
1018+
1019+
const auto *GV = Equiv->getGlobalValue();
1020+
1021+
// A PLT entry is not needed for dso_local globals.
1022+
if (GV->isDSOLocal() || GV->isImplicitDSOLocal())
1023+
return MCSymbolRefExpr::create(TM.getSymbol(GV), getContext());
1024+
1025+
return MCSymbolRefExpr::create(TM.getSymbol(GV), PLTRelativeVariantKind,
1026+
getContext());
1027+
}
1028+
10101029
MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const {
10111030
// Use ".GCC.command.line" since this feature is to support clang's
10121031
// -frecord-gcc-switches which in turn attempts to mimic GCC's switch of the

0 commit comments

Comments
 (0)