diff options
author | Leonard Chan <leonardchan@google.com> | 2020-04-01 15:25:04 -0700 |
---|---|---|
committer | Leonard Chan <leonardchan@google.com> | 2020-11-19 10:26:17 -0800 |
commit | a97f62837f59620a60b3ed9f29568440c7d0553a (patch) | |
tree | 360eb006af37d0abe1f40757db6f4da7b5246984 /llvm/lib/IR/Constants.cpp | |
parent | 2f3adc54b57356cd207ded2ec2d7f4e1273da0ff (diff) | |
download | llvm-a97f62837f59620a60b3ed9f29568440c7d0553a.zip llvm-a97f62837f59620a60b3ed9f29568440c7d0553a.tar.gz llvm-a97f62837f59620a60b3ed9f29568440c7d0553a.tar.bz2 |
[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
Diffstat (limited to 'llvm/lib/IR/Constants.cpp')
-rw-r--r-- | llvm/lib/IR/Constants.cpp | 62 |
1 files changed, 60 insertions, 2 deletions
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index 3d8555b..732d6e6 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -509,6 +509,9 @@ void llvm::deleteConstant(Constant *C) { case Constant::BlockAddressVal: delete static_cast<BlockAddress *>(C); break; + case Constant::DSOLocalEquivalentVal: + delete static_cast<DSOLocalEquivalent *>(C); + break; case Constant::UndefValueVal: delete static_cast<UndefValue *>(C); break; @@ -654,10 +657,17 @@ bool Constant::needsRelocation() const { return false; // Relative pointers do not need to be dynamically relocated. - if (auto *LHSGV = dyn_cast<GlobalValue>(LHSOp0->stripPointerCasts())) - if (auto *RHSGV = dyn_cast<GlobalValue>(RHSOp0->stripPointerCasts())) + if (auto *RHSGV = + dyn_cast<GlobalValue>(RHSOp0->stripInBoundsConstantOffsets())) { + auto *LHS = LHSOp0->stripInBoundsConstantOffsets(); + if (auto *LHSGV = dyn_cast<GlobalValue>(LHS)) { if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal()) 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; } +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. // |