Movatterモバイル変換


[0]ホーム

URL:


LLVM 20.0.0git
ControlFlowUtils.cpp
Go to the documentation of this file.
1//===- ControlFlowUtils.cpp - Control Flow Utilities -----------------------==//
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// Utilities to manipulate the CFG and restore SSA for the new control flow.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/Transforms/Utils/ControlFlowUtils.h"
14#include "llvm/ADT/SetVector.h"
15#include "llvm/ADT/SmallSet.h"
16#include "llvm/Analysis/DomTreeUpdater.h"
17#include "llvm/IR/Constants.h"
18#include "llvm/IR/Instructions.h"
19#include "llvm/IR/ValueHandle.h"
20#include "llvm/Transforms/Utils/Local.h"
21
22#define DEBUG_TYPE "control-flow-hub"
23
24using namespacellvm;
25
26usingBBPredicates =DenseMap<BasicBlock *, Instruction *>;
27usingEdgeDescriptor =ControlFlowHub::BranchDescriptor;
28
29// Redirects the terminator of the incoming block to the first guard block in
30// the hub. Returns the branch condition from `BB` if it exits.
31// - If only one of Succ0 or Succ1 is not null, the corresponding branch
32// successor is redirected to the FirstGuardBlock.
33// - Else both are not null, and branch is replaced with an unconditional
34// branch to the FirstGuardBlock.
35staticValue *redirectToHub(BasicBlock *BB,BasicBlock *Succ0,
36BasicBlock *Succ1,BasicBlock *FirstGuardBlock) {
37assert(isa<BranchInst>(BB->getTerminator()) &&
38"Only support branch terminator.");
39auto *Branch = cast<BranchInst>(BB->getTerminator());
40auto *Condition = Branch->isConditional() ? Branch->getCondition() :nullptr;
41
42assert(Succ0 || Succ1);
43
44if (Branch->isUnconditional()) {
45assert(Succ0 == Branch->getSuccessor(0));
46assert(!Succ1);
47 Branch->setSuccessor(0, FirstGuardBlock);
48 }else {
49assert(!Succ1 || Succ1 == Branch->getSuccessor(1));
50if (Succ0 && !Succ1) {
51 Branch->setSuccessor(0, FirstGuardBlock);
52 }elseif (Succ1 && !Succ0) {
53 Branch->setSuccessor(1, FirstGuardBlock);
54 }else {
55 Branch->eraseFromParent();
56BranchInst::Create(FirstGuardBlock, BB);
57 }
58 }
59
60return Condition;
61}
62
63// Setup the branch instructions for guard blocks.
64//
65// Each guard block terminates in a conditional branch that transfers
66// control to the corresponding outgoing block or the next guard
67// block. The last guard block has two outgoing blocks as successors.
68staticvoidsetupBranchForGuard(ArrayRef<BasicBlock *> GuardBlocks,
69ArrayRef<BasicBlock *> Outgoing,
70BBPredicates &GuardPredicates) {
71assert(Outgoing.size() > 1);
72assert(GuardBlocks.size() == Outgoing.size() - 1);
73intI = 0;
74for (int E = GuardBlocks.size() - 1;I != E; ++I) {
75BasicBlock *Out = Outgoing[I];
76BranchInst::Create(Out, GuardBlocks[I + 1], GuardPredicates[Out],
77 GuardBlocks[I]);
78 }
79BasicBlock *Out = Outgoing[I];
80BranchInst::Create(Out, Outgoing[I + 1], GuardPredicates[Out],
81 GuardBlocks[I]);
82}
83
84// Assign an index to each outgoing block. At the corresponding guard
85// block, compute the branch condition by comparing this index.
86staticvoidcalcPredicateUsingInteger(ArrayRef<EdgeDescriptor> Branches,
87ArrayRef<BasicBlock *> Outgoing,
88ArrayRef<BasicBlock *> GuardBlocks,
89BBPredicates &GuardPredicates) {
90LLVMContext &Context = GuardBlocks.front()->getContext();
91BasicBlock *FirstGuardBlock = GuardBlocks.front();
92Type *Int32Ty =Type::getInt32Ty(Context);
93
94auto *Phi =PHINode::Create(Int32Ty, Branches.size(),"merged.bb.idx",
95 FirstGuardBlock);
96
97for (auto [BB, Succ0, Succ1] : Branches) {
98Value *Condition =redirectToHub(BB, Succ0, Succ1, FirstGuardBlock);
99Value *IncomingId =nullptr;
100if (Succ0 && Succ1) {
101auto Succ0Iter =find(Outgoing, Succ0);
102auto Succ1Iter =find(Outgoing, Succ1);
103Value *Id0 =
104 ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), Succ0Iter));
105Value *Id1 =
106 ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), Succ1Iter));
107 IncomingId =SelectInst::Create(Condition, Id0, Id1,"target.bb.idx",
108 BB->getTerminator()->getIterator());
109 }else {
110// Get the index of the non-null successor.
111auto SuccIter = Succ0 ?find(Outgoing, Succ0) :find(Outgoing, Succ1);
112 IncomingId =
113 ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), SuccIter));
114 }
115 Phi->addIncoming(IncomingId, BB);
116 }
117
118for (intI = 0, E = Outgoing.size() - 1;I != E; ++I) {
119BasicBlock *Out = Outgoing[I];
120LLVM_DEBUG(dbgs() <<"Creating integer guard for " << Out->getName()
121 <<"\n");
122auto *Cmp = ICmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, Phi,
123 ConstantInt::get(Int32Ty,I),
124 Out->getName() +".predicate", GuardBlocks[I]);
125 GuardPredicates[Out] = Cmp;
126 }
127}
128
129// Determine the branch condition to be used at each guard block from the
130// original boolean values.
131staticvoidcalcPredicateUsingBooleans(
132ArrayRef<EdgeDescriptor> Branches,ArrayRef<BasicBlock *> Outgoing,
133SmallVectorImpl<BasicBlock *> &GuardBlocks,BBPredicates &GuardPredicates,
134SmallVectorImpl<WeakVH> &DeletionCandidates) {
135LLVMContext &Context = GuardBlocks.front()->getContext();
136auto *BoolTrue =ConstantInt::getTrue(Context);
137auto *BoolFalse =ConstantInt::getFalse(Context);
138BasicBlock *FirstGuardBlock = GuardBlocks.front();
139
140// The predicate for the last outgoing is trivially true, and so we
141// process only the first N-1 successors.
142for (intI = 0, E = Outgoing.size() - 1;I != E; ++I) {
143BasicBlock *Out = Outgoing[I];
144LLVM_DEBUG(dbgs() <<"Creating boolean guard for " << Out->getName()
145 <<"\n");
146
147auto *Phi =
148PHINode::Create(Type::getInt1Ty(Context), Branches.size(),
149StringRef("Guard.") + Out->getName(), FirstGuardBlock);
150 GuardPredicates[Out] = Phi;
151 }
152
153for (auto [BB, Succ0, Succ1] : Branches) {
154Value *Condition =redirectToHub(BB, Succ0, Succ1, FirstGuardBlock);
155
156// Optimization: Consider an incoming block A with both successors
157// Succ0 and Succ1 in the set of outgoing blocks. The predicates
158// for Succ0 and Succ1 complement each other. If Succ0 is visited
159// first in the loop below, control will branch to Succ0 using the
160// corresponding predicate. But if that branch is not taken, then
161// control must reach Succ1, which means that the incoming value of
162// the predicate from `BB` is true for Succ1.
163bool OneSuccessorDone =false;
164for (intI = 0, E = Outgoing.size() - 1;I != E; ++I) {
165BasicBlock *Out = Outgoing[I];
166PHINode *Phi = cast<PHINode>(GuardPredicates[Out]);
167if (Out != Succ0 && Out != Succ1) {
168 Phi->addIncoming(BoolFalse, BB);
169 }elseif (!Succ0 || !Succ1 || OneSuccessorDone) {
170// Optimization: When only one successor is an outgoing block,
171// the incoming predicate from `BB` is always true.
172 Phi->addIncoming(BoolTrue, BB);
173 }else {
174assert(Succ0 && Succ1);
175if (Out == Succ0) {
176 Phi->addIncoming(Condition, BB);
177 }else {
178Value *Inverted =invertCondition(Condition);
179 DeletionCandidates.push_back(Condition);
180 Phi->addIncoming(Inverted, BB);
181 }
182 OneSuccessorDone =true;
183 }
184 }
185 }
186}
187
188// Capture the existing control flow as guard predicates, and redirect
189// control flow from \p Incoming block through the \p GuardBlocks to the
190// \p Outgoing blocks.
191//
192// There is one guard predicate for each outgoing block OutBB. The
193// predicate represents whether the hub should transfer control flow
194// to OutBB. These predicates are NOT ORTHOGONAL. The Hub evaluates
195// them in the same order as the Outgoing set-vector, and control
196// branches to the first outgoing block whose predicate evaluates to true.
197//
198// The last guard block has two outgoing blocks as successors since the
199// condition for the final outgoing block is trivially true. So we create one
200// less block (including the first guard block) than the number of outgoing
201// blocks.
202staticvoidconvertToGuardPredicates(
203ArrayRef<EdgeDescriptor> Branches,ArrayRef<BasicBlock *> Outgoing,
204SmallVectorImpl<BasicBlock *> &GuardBlocks,
205SmallVectorImpl<WeakVH> &DeletionCandidates,constStringRef Prefix,
206 std::optional<unsigned> MaxControlFlowBooleans) {
207BBPredicates GuardPredicates;
208Function *F = Outgoing.front()->getParent();
209
210for (intI = 0, E = Outgoing.size() - 1;I != E; ++I)
211 GuardBlocks.push_back(
212BasicBlock::Create(F->getContext(), Prefix +".guard",F));
213
214// When we are using an integer to record which target block to jump to, we
215// are creating less live values, actually we are using one single integer to
216// store the index of the target block. When we are using booleans to store
217// the branching information, we need (N-1) boolean values, where N is the
218// number of outgoing block.
219if (!MaxControlFlowBooleans || Outgoing.size() <= *MaxControlFlowBooleans)
220calcPredicateUsingBooleans(Branches, Outgoing, GuardBlocks, GuardPredicates,
221 DeletionCandidates);
222else
223calcPredicateUsingInteger(Branches, Outgoing, GuardBlocks, GuardPredicates);
224
225setupBranchForGuard(GuardBlocks, Outgoing, GuardPredicates);
226}
227
228// After creating a control flow hub, the operands of PHINodes in an outgoing
229// block Out no longer match the predecessors of that block. Predecessors of Out
230// that are incoming blocks to the hub are now replaced by just one edge from
231// the hub. To match this new control flow, the corresponding values from each
232// PHINode must now be moved a new PHINode in the first guard block of the hub.
233//
234// This operation cannot be performed with SSAUpdater, because it involves one
235// new use: If the block Out is in the list of Incoming blocks, then the newly
236// created PHI in the Hub will use itself along that edge from Out to Hub.
237staticvoidreconnectPhis(BasicBlock *Out,BasicBlock *GuardBlock,
238ArrayRef<EdgeDescriptor>Incoming,
239BasicBlock *FirstGuardBlock) {
240autoI = Out->begin();
241while (I != Out->end() && isa<PHINode>(I)) {
242auto *Phi = cast<PHINode>(I);
243auto *NewPhi =
244PHINode::Create(Phi->getType(),Incoming.size(),
245 Phi->getName() +".moved", FirstGuardBlock->begin());
246bool AllUndef =true;
247for (auto [BB, Succ0, Succ1] :Incoming) {
248Value *V =PoisonValue::get(Phi->getType());
249if (BB == Out) {
250 V = NewPhi;
251 }elseif (Phi->getBasicBlockIndex(BB) != -1) {
252 V = Phi->removeIncomingValue(BB,false);
253 AllUndef &= isa<UndefValue>(V);
254 }
255 NewPhi->addIncoming(V, BB);
256 }
257assert(NewPhi->getNumIncomingValues() ==Incoming.size());
258Value *NewV = NewPhi;
259if (AllUndef) {
260 NewPhi->eraseFromParent();
261 NewV =PoisonValue::get(Phi->getType());
262 }
263if (Phi->getNumOperands() == 0) {
264 Phi->replaceAllUsesWith(NewV);
265I = Phi->eraseFromParent();
266continue;
267 }
268 Phi->addIncoming(NewV, GuardBlock);
269 ++I;
270 }
271}
272
273BasicBlock *ControlFlowHub::finalize(
274DomTreeUpdater *DTU,SmallVectorImpl<BasicBlock *> &GuardBlocks,
275constStringRef Prefix, std::optional<unsigned> MaxControlFlowBooleans) {
276#ifndef NDEBUG
277SmallSet<BasicBlock *, 8>Incoming;
278#endif
279SetVector<BasicBlock *> Outgoing;
280
281for (auto [BB, Succ0, Succ1] :Branches) {
282#ifndef NDEBUG
283assert(Incoming.insert(BB).second &&"Duplicate entry for incoming block.");
284#endif
285if (Succ0)
286 Outgoing.insert(Succ0);
287if (Succ1)
288 Outgoing.insert(Succ1);
289 }
290
291if (Outgoing.size() < 2)
292return Outgoing.front();
293
294SmallVector<DominatorTree::UpdateType, 16> Updates;
295if (DTU) {
296for (auto [BB, Succ0, Succ1] :Branches) {
297if (Succ0)
298 Updates.push_back({DominatorTree::Delete, BB, Succ0});
299if (Succ1)
300 Updates.push_back({DominatorTree::Delete, BB, Succ1});
301 }
302 }
303
304SmallVector<WeakVH, 8> DeletionCandidates;
305convertToGuardPredicates(Branches, Outgoing.getArrayRef(), GuardBlocks,
306 DeletionCandidates, Prefix, MaxControlFlowBooleans);
307BasicBlock *FirstGuardBlock = GuardBlocks.front();
308
309// Update the PHINodes in each outgoing block to match the new control flow.
310for (intI = 0, E = GuardBlocks.size();I != E; ++I)
311reconnectPhis(Outgoing[I], GuardBlocks[I],Branches, FirstGuardBlock);
312// Process the Nth (last) outgoing block with the (N-1)th (last) guard block.
313reconnectPhis(Outgoing.back(), GuardBlocks.back(),Branches, FirstGuardBlock);
314
315if (DTU) {
316int NumGuards = GuardBlocks.size();
317
318for (auto [BB, Succ0, Succ1] :Branches)
319 Updates.push_back({DominatorTree::Insert, BB, FirstGuardBlock});
320
321for (intI = 0;I != NumGuards - 1; ++I) {
322 Updates.push_back({DominatorTree::Insert, GuardBlocks[I], Outgoing[I]});
323 Updates.push_back(
324 {DominatorTree::Insert, GuardBlocks[I], GuardBlocks[I + 1]});
325 }
326// The second successor of the last guard block is an outgoing block instead
327// of having a "next" guard block.
328 Updates.push_back({DominatorTree::Insert, GuardBlocks[NumGuards - 1],
329 Outgoing[NumGuards - 1]});
330 Updates.push_back({DominatorTree::Insert, GuardBlocks[NumGuards - 1],
331 Outgoing[NumGuards]});
332 DTU->applyUpdates(Updates);
333 }
334
335for (autoI : DeletionCandidates) {
336if (I->use_empty())
337if (auto *Inst = dyn_cast_or_null<Instruction>(I))
338 Inst->eraseFromParent();
339 }
340
341return FirstGuardBlock;
342}
Constants.h
This file contains the declarations for the subclasses of Constant, which represent the different fla...
calcPredicateUsingBooleans
static void calcPredicateUsingBooleans(ArrayRef< EdgeDescriptor > Branches, ArrayRef< BasicBlock * > Outgoing, SmallVectorImpl< BasicBlock * > &GuardBlocks, BBPredicates &GuardPredicates, SmallVectorImpl< WeakVH > &DeletionCandidates)
Definition:ControlFlowUtils.cpp:131
setupBranchForGuard
static void setupBranchForGuard(ArrayRef< BasicBlock * > GuardBlocks, ArrayRef< BasicBlock * > Outgoing, BBPredicates &GuardPredicates)
Definition:ControlFlowUtils.cpp:68
reconnectPhis
static void reconnectPhis(BasicBlock *Out, BasicBlock *GuardBlock, ArrayRef< EdgeDescriptor > Incoming, BasicBlock *FirstGuardBlock)
Definition:ControlFlowUtils.cpp:237
calcPredicateUsingInteger
static void calcPredicateUsingInteger(ArrayRef< EdgeDescriptor > Branches, ArrayRef< BasicBlock * > Outgoing, ArrayRef< BasicBlock * > GuardBlocks, BBPredicates &GuardPredicates)
Definition:ControlFlowUtils.cpp:86
redirectToHub
static Value * redirectToHub(BasicBlock *BB, BasicBlock *Succ0, BasicBlock *Succ1, BasicBlock *FirstGuardBlock)
Definition:ControlFlowUtils.cpp:35
convertToGuardPredicates
static void convertToGuardPredicates(ArrayRef< EdgeDescriptor > Branches, ArrayRef< BasicBlock * > Outgoing, SmallVectorImpl< BasicBlock * > &GuardBlocks, SmallVectorImpl< WeakVH > &DeletionCandidates, const StringRef Prefix, std::optional< unsigned > MaxControlFlowBooleans)
Definition:ControlFlowUtils.cpp:202
ControlFlowUtils.h
LLVM_DEBUG
#define LLVM_DEBUG(...)
Definition:Debug.h:106
DomTreeUpdater.h
Instructions.h
F
#define F(x, y, z)
Definition:MD5.cpp:55
I
#define I(x, y, z)
Definition:MD5.cpp:58
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
SetVector.h
This file implements a set that has insertion order iteration characteristics.
SmallSet.h
This file defines the SmallSet class.
Local.h
ValueHandle.h
llvm::ArrayRef
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition:ArrayRef.h:41
llvm::ArrayRef::front
const T & front() const
front - Get the first element.
Definition:ArrayRef.h:171
llvm::ArrayRef::size
size_t size() const
size - Get the array size.
Definition:ArrayRef.h:168
llvm::ArrayRef::begin
iterator begin() const
Definition:ArrayRef.h:156
llvm::BasicBlock
LLVM Basic Block Representation.
Definition:BasicBlock.h:61
llvm::BasicBlock::end
iterator end()
Definition:BasicBlock.h:474
llvm::BasicBlock::begin
iterator begin()
Instruction iterator methods.
Definition:BasicBlock.h:461
llvm::BasicBlock::Create
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition:BasicBlock.h:213
llvm::BasicBlock::getTerminator
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
Definition:BasicBlock.h:240
llvm::BranchInst::Create
static BranchInst * Create(BasicBlock *IfTrue, InsertPosition InsertBefore=nullptr)
Definition:Instructions.h:3072
llvm::ConstantInt::getTrue
static ConstantInt * getTrue(LLVMContext &Context)
Definition:Constants.cpp:866
llvm::ConstantInt::getFalse
static ConstantInt * getFalse(LLVMContext &Context)
Definition:Constants.cpp:873
llvm::DenseMap
Definition:DenseMap.h:727
llvm::DomTreeUpdater
Definition:DomTreeUpdater.h:30
llvm::DominatorTreeBase< BasicBlock, false >::Delete
static constexpr UpdateKind Delete
Definition:GenericDomTree.h:253
llvm::DominatorTreeBase< BasicBlock, false >::Insert
static constexpr UpdateKind Insert
Definition:GenericDomTree.h:252
llvm::Function
Definition:Function.h:63
llvm::GenericDomTreeUpdater::applyUpdates
void applyUpdates(ArrayRef< UpdateT > Updates)
Submit updates to all available trees.
Definition:GenericDomTreeUpdaterImpl.h:59
llvm::LLVMContext
This is an important class for using LLVM in a threaded context.
Definition:LLVMContext.h:67
llvm::PHINode
Definition:Instructions.h:2600
llvm::PHINode::Create
static PHINode * Create(Type *Ty, unsigned NumReservedValues, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Constructors - NumReservedValues is a hint for the number of incoming edges that this phi node will h...
Definition:Instructions.h:2635
llvm::PoisonValue::get
static PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
Definition:Constants.cpp:1878
llvm::SelectInst::Create
static SelectInst * Create(Value *C, Value *S1, Value *S2, const Twine &NameStr="", InsertPosition InsertBefore=nullptr, Instruction *MDFrom=nullptr)
Definition:Instructions.h:1682
llvm::SetVector
A vector that has set insertion semantics.
Definition:SetVector.h:57
llvm::SetVector::getArrayRef
ArrayRef< value_type > getArrayRef() const
Definition:SetVector.h:84
llvm::SetVector::size
size_type size() const
Determine the number of elements in the SetVector.
Definition:SetVector.h:98
llvm::SetVector::front
const value_type & front() const
Return the first element of the SetVector.
Definition:SetVector.h:143
llvm::SetVector::back
const value_type & back() const
Return the last element of the SetVector.
Definition:SetVector.h:149
llvm::SetVector::insert
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition:SetVector.h:162
llvm::SmallSet
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition:SmallSet.h:132
llvm::SmallVectorBase::size
size_t size() const
Definition:SmallVector.h:78
llvm::SmallVectorImpl
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition:SmallVector.h:573
llvm::SmallVectorTemplateBase::push_back
void push_back(const T &Elt)
Definition:SmallVector.h:413
llvm::SmallVectorTemplateCommon::front
reference front()
Definition:SmallVector.h:299
llvm::SmallVectorTemplateCommon::back
reference back()
Definition:SmallVector.h:308
llvm::SmallVector
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition:SmallVector.h:1196
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition:StringRef.h:51
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition:Type.h:45
llvm::Type::getInt1Ty
static IntegerType * getInt1Ty(LLVMContext &C)
llvm::Type::getInt32Ty
static IntegerType * getInt32Ty(LLVMContext &C)
llvm::Value
LLVM Value Representation.
Definition:Value.h:74
llvm::Value::getName
StringRef getName() const
Return a constant reference to the value's name.
Definition:Value.cpp:309
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition:AddressRanges.h:18
llvm::find
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition:STLExtras.h:1759
llvm::dbgs
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition:Debug.cpp:163
llvm::invertCondition
Value * invertCondition(Value *Condition)
Invert the given true/false value, possibly reusing an existing copy.
Definition:Local.cpp:4282
llvm::ControlFlowHub::BranchDescriptor
Definition:ControlFlowUtils.h:98
llvm::ControlFlowHub::finalize
BasicBlock * finalize(DomTreeUpdater *DTU, SmallVectorImpl< BasicBlock * > &GuardBlocks, const StringRef Prefix, std::optional< unsigned > MaxControlFlowBooleans=std::nullopt)
Definition:ControlFlowUtils.cpp:273
llvm::ControlFlowHub::Branches
SmallVector< BranchDescriptor > Branches
Definition:ControlFlowUtils.h:118
llvm::Incoming
Incoming for lane maks phi as machine instruction, incoming register Reg and incoming block Block are...
Definition:SILowerI1Copies.h:25

Generated on Fri Jul 18 2025 16:25:09 for LLVM by doxygen 1.9.6
[8]ページ先頭

©2009-2025 Movatter.jp