LLVM 20.0.0git
TypeMetadataUtils.cpp
Go to the documentation of this file.
1//===- TypeMetadataUtils.cpp - Utilities related to type metadata ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains functions that make it easier to manipulate type metadata
10// for devirtualization.
11//
12//===----------------------------------------------------------------------===//
13
15#include "llvm/IR/Constants.h"
16#include "llvm/IR/Dominators.h"
19#include "llvm/IR/Module.h"
20
21using namespace llvm;
22
23// Search for virtual calls that call FPtr and add them to DevirtCalls.
24static void
26 bool *HasNonCallUses, Value *FPtr, uint64_t Offset,
27 const CallInst *CI, DominatorTree &DT) {
28 for (const Use &U : FPtr->uses()) {
29 Instruction *User = cast<Instruction>(U.getUser());
30 // Ignore this instruction if it is not dominated by the type intrinsic
31 // being analyzed. Otherwise we may transform a call sharing the same
32 // vtable pointer incorrectly. Specifically, this situation can arise
33 // after indirect call promotion and inlining, where we may have uses
34 // of the vtable pointer guarded by a function pointer check, and a fallback
35 // indirect call.
36 if (CI->getFunction() != User->getFunction())
37 continue;
38 if (!DT.dominates(CI, User))
39 continue;
40 if (isa<BitCastInst>(User)) {
41 findCallsAtConstantOffset(DevirtCalls, HasNonCallUses, User, Offset, CI,
42 DT);
43 } else if (auto *CI = dyn_cast<CallInst>(User)) {
44 DevirtCalls.push_back({Offset, *CI});
45 } else if (auto *II = dyn_cast<InvokeInst>(User)) {
46 DevirtCalls.push_back({Offset, *II});
47 } else if (HasNonCallUses) {
48 *HasNonCallUses = true;
49 }
50 }
51}
52
53// Search for virtual calls that load from VPtr and add them to DevirtCalls.
55 const Module *M, SmallVectorImpl<DevirtCallSite> &DevirtCalls, Value *VPtr,
56 int64_t Offset, const CallInst *CI, DominatorTree &DT) {
57 for (const Use &U : VPtr->uses()) {
58 Value *User = U.getUser();
59 if (isa<BitCastInst>(User)) {
60 findLoadCallsAtConstantOffset(M, DevirtCalls, User, Offset, CI, DT);
61 } else if (isa<LoadInst>(User)) {
62 findCallsAtConstantOffset(DevirtCalls, nullptr, User, Offset, CI, DT);
63 } else if (auto GEP = dyn_cast<GetElementPtrInst>(User)) {
64 // Take into account the GEP offset.
65 if (VPtr == GEP->getPointerOperand() && GEP->hasAllConstantIndices()) {
66 SmallVector<Value *, 8> Indices(drop_begin(GEP->operands()));
67 int64_t GEPOffset = M->getDataLayout().getIndexedOffsetInType(
68 GEP->getSourceElementType(), Indices);
69 findLoadCallsAtConstantOffset(M, DevirtCalls, User, Offset + GEPOffset,
70 CI, DT);
71 }
72 } else if (auto *Call = dyn_cast<CallInst>(User)) {
73 if (Call->getIntrinsicID() == llvm::Intrinsic::load_relative) {
74 if (auto *LoadOffset = dyn_cast<ConstantInt>(Call->getOperand(1))) {
75 findCallsAtConstantOffset(DevirtCalls, nullptr, User,
76 Offset + LoadOffset->getSExtValue(), CI,
77 DT);
78 }
79 }
80 }
81 }
82}
83
86 SmallVectorImpl<CallInst *> &Assumes, const CallInst *CI,
87 DominatorTree &DT) {
88 assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test ||
90 Intrinsic::public_type_test);
91
92 const Module *M = CI->getParent()->getParent()->getParent();
93
94 // Find llvm.assume intrinsics for this llvm.type.test call.
95 for (const Use &CIU : CI->uses())
96 if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser()))
97 Assumes.push_back(Assume);
98
99 // If we found any, search for virtual calls based on %p and add them to
100 // DevirtCalls.
101 if (!Assumes.empty())
103 M, DevirtCalls, CI->getArgOperand(0)->stripPointerCasts(), 0, CI, DT);
104}
105
109 SmallVectorImpl<Instruction *> &Preds, bool &HasNonCallUses,
110 const CallInst *CI, DominatorTree &DT) {
112 Intrinsic::type_checked_load ||
114 Intrinsic::type_checked_load_relative);
115
116 auto *Offset = dyn_cast<ConstantInt>(CI->getArgOperand(1));
117 if (!Offset) {
118 HasNonCallUses = true;
119 return;
120 }
121
122 for (const Use &U : CI->uses()) {
123 auto CIU = U.getUser();
124 if (auto EVI = dyn_cast<ExtractValueInst>(CIU)) {
125 if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 0) {
126 LoadedPtrs.push_back(EVI);
127 continue;
128 }
129 if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 1) {
130 Preds.push_back(EVI);
131 continue;
132 }
133 }
134 HasNonCallUses = true;
135 }
136
137 for (Value *LoadedPtr : LoadedPtrs)
138 findCallsAtConstantOffset(DevirtCalls, &HasNonCallUses, LoadedPtr,
139 Offset->getZExtValue(), CI, DT);
140}
141
143 Constant *TopLevelGlobal) {
144 // TODO: Ideally it would be the caller who knows if it's appropriate to strip
145 // the DSOLocalEquicalent. More generally, it would feel more appropriate to
146 // have two functions that handle absolute and relative pointers separately.
147 if (auto *Equiv = dyn_cast<DSOLocalEquivalent>(I))
148 I = Equiv->getGlobalValue();
149
150 if (I->getType()->isPointerTy()) {
151 if (Offset == 0)
152 return I;
153 return nullptr;
154 }
155
156 const DataLayout &DL = M.getDataLayout();
157
158 if (auto *C = dyn_cast<ConstantStruct>(I)) {
159 const StructLayout *SL = DL.getStructLayout(C->getType());
160 if (Offset >= SL->getSizeInBytes())
161 return nullptr;
162
163 unsigned Op = SL->getElementContainingOffset(Offset);
164 return getPointerAtOffset(cast<Constant>(I->getOperand(Op)),
165 Offset - SL->getElementOffset(Op), M,
166 TopLevelGlobal);
167 }
168 if (auto *C = dyn_cast<ConstantArray>(I)) {
169 ArrayType *VTableTy = C->getType();
170 uint64_t ElemSize = DL.getTypeAllocSize(VTableTy->getElementType());
171
172 unsigned Op = Offset / ElemSize;
173 if (Op >= C->getNumOperands())
174 return nullptr;
175
176 return getPointerAtOffset(cast<Constant>(I->getOperand(Op)),
177 Offset % ElemSize, M, TopLevelGlobal);
178 }
179
180 // Relative-pointer support starts here.
181 if (auto *CI = dyn_cast<ConstantInt>(I)) {
182 if (Offset == 0 && CI->isZero()) {
183 return I;
184 }
185 }
186 if (auto *C = dyn_cast<ConstantExpr>(I)) {
187 switch (C->getOpcode()) {
188 case Instruction::Trunc:
189 case Instruction::PtrToInt:
190 return getPointerAtOffset(cast<Constant>(C->getOperand(0)), Offset, M,
191 TopLevelGlobal);
192 case Instruction::Sub: {
193 auto *Operand0 = cast<Constant>(C->getOperand(0));
194 auto *Operand1 = cast<Constant>(C->getOperand(1));
195
196 auto StripGEP = [](Constant *C) {
197 auto *CE = dyn_cast<ConstantExpr>(C);
198 if (!CE)
199 return C;
200 if (CE->getOpcode() != Instruction::GetElementPtr)
201 return C;
202 return CE->getOperand(0);
203 };
204 auto *Operand1TargetGlobal = StripGEP(getPointerAtOffset(Operand1, 0, M));
205
206 // Check that in the "sub (@a, @b)" expression, @b points back to the top
207 // level global (or a GEP thereof) that we're processing. Otherwise bail.
208 if (Operand1TargetGlobal != TopLevelGlobal)
209 return nullptr;
210
211 return getPointerAtOffset(Operand0, Offset, M, TopLevelGlobal);
212 }
213 default:
214 return nullptr;
215 }
216 }
217 return nullptr;
218}
219
220std::pair<Function *, Constant *>
222 Module &M) {
224 if (!Ptr)
225 return std::pair<Function *, Constant *>(nullptr, nullptr);
226
227 auto C = Ptr->stripPointerCasts();
228 // Make sure this is a function or alias to a function.
229 auto Fn = dyn_cast<Function>(C);
230 auto A = dyn_cast<GlobalAlias>(C);
231 if (!Fn && A)
232 Fn = dyn_cast<Function>(A->getAliasee());
233
234 if (!Fn)
235 return std::pair<Function *, Constant *>(nullptr, nullptr);
236
237 return std::pair<Function *, Constant *>(Fn, C);
238}
239
241 auto *PtrExpr = dyn_cast<ConstantExpr>(U);
242 if (!PtrExpr || PtrExpr->getOpcode() != Instruction::PtrToInt)
243 return;
244
245 for (auto *PtrToIntUser : PtrExpr->users()) {
246 auto *SubExpr = dyn_cast<ConstantExpr>(PtrToIntUser);
247 if (!SubExpr || SubExpr->getOpcode() != Instruction::Sub)
248 return;
249
250 SubExpr->replaceNonMetadataUsesWith(
251 ConstantInt::get(SubExpr->getType(), 0));
252 }
253}
254
256 for (auto *U : C->users()) {
257 if (auto *Equiv = dyn_cast<DSOLocalEquivalent>(U))
259 else
261 }
262}
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Hexagon Common GEP
Module.h This file contains the declarations for the Module class.
#define I(x, y, z)
Definition: MD5.cpp:58
uint64_t IntrinsicInst * II
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static void findLoadCallsAtConstantOffset(const Module *M, SmallVectorImpl< DevirtCallSite > &DevirtCalls, Value *VPtr, int64_t Offset, const CallInst *CI, DominatorTree &DT)
static void findCallsAtConstantOffset(SmallVectorImpl< DevirtCallSite > &DevirtCalls, bool *HasNonCallUses, Value *FPtr, uint64_t Offset, const CallInst *CI, DominatorTree &DT)
static void replaceRelativePointerUserWithZero(User *U)
Class to represent array types.
Definition: DerivedTypes.h:395
Type * getElementType() const
Definition: DerivedTypes.h:408
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Definition: InstrTypes.h:1349
Value * getArgOperand(unsigned i) const
Definition: InstrTypes.h:1294
This class represents a function call, abstracting a target machine's calling convention.
This is an important base class in LLVM.
Definition: Constant.h:42
This class represents an Operation in the Expression.
A parsed version of the target data layout string in and methods for querying it.
Definition: DataLayout.h:63
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition: Dominators.h:162
bool dominates(const BasicBlock *BB, const Use &U) const
Return true if the (end of the) basic block BB dominates the use U.
Definition: Dominators.cpp:122
Intrinsic::ID getIntrinsicID() const LLVM_READONLY
getIntrinsicID - This method returns the ID number of the specified function, or Intrinsic::not_intri...
Definition: Function.h:251
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
const Function * getFunction() const
Return the function this instruction belongs to.
Definition: Instruction.cpp:72
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
bool empty() const
Definition: SmallVector.h:81
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:573
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
Used to lazily calculate structure layout information for a target machine, based on the DataLayout s...
Definition: DataLayout.h:567
TypeSize getSizeInBytes() const
Definition: DataLayout.h:574
unsigned getElementContainingOffset(uint64_t FixedOffset) const
Given a valid byte offset into the structure, returns the structure index that contains it.
Definition: DataLayout.cpp:92
TypeSize getElementOffset(unsigned Idx) const
Definition: DataLayout.h:596
A Use represents the edge between a Value definition and its users.
Definition: Use.h:43
LLVM Value Representation.
Definition: Value.h:74
const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition: Value.cpp:694
iterator_range< use_iterator > uses()
Definition: Value.h:376
const ParentTy * getParent() const
Definition: ilist_node.h:32
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition: STLExtras.h:329
@ Offset
Definition: DWP.cpp:480
void findDevirtualizableCallsForTypeCheckedLoad(SmallVectorImpl< DevirtCallSite > &DevirtCalls, SmallVectorImpl< Instruction * > &LoadedPtrs, SmallVectorImpl< Instruction * > &Preds, bool &HasNonCallUses, const CallInst *CI, DominatorTree &DT)
Given a call to the intrinsic @llvm.type.checked.load, find all devirtualizable call sites based on t...
void replaceRelativePointerUsersWithZero(Constant *C)
Finds the same "relative pointer" pattern as described above, where the target is C,...
void findDevirtualizableCallsForTypeTest(SmallVectorImpl< DevirtCallSite > &DevirtCalls, SmallVectorImpl< CallInst * > &Assumes, const CallInst *CI, DominatorTree &DT)
Given a call to the intrinsic @llvm.type.test, find all devirtualizable call sites based on the call ...
Constant * getPointerAtOffset(Constant *I, uint64_t Offset, Module &M, Constant *TopLevelGlobal=nullptr)
Processes a Constant recursively looking into elements of arrays, structs and expressions to find a t...
std::pair< Function *, Constant * > getFunctionAtVTableOffset(GlobalVariable *GV, uint64_t Offset, Module &M)
Given a vtable and a specified offset, returns the function and the trivial pointer at the specified ...