Skip to content

Commit 4fe4e68

Browse files
committed
[InstCombine] Iterative replacement in PtrReplacer
This patch enhances the PtrReplacer as follows: 1. Users are now collected iteratively to be generous on the stack. In the case of PHIs with incoming values which have not yet been visited, they are pushed back into the stack for reconsideration. 2. Replace users of the pointer root in a reverse-postorder traversal, instead of a simple traversal over the collected users. This reordering ensures that the uses of an instruction are replaced before replacing the instruction itself. 3. During the replacement of PHI, use the same incoming value if it does not have a replacement. This patch specifically fixes the case when an incoming value of a PHI is addrspacecasted. So it is an NFC if the datalayout is unknown.
1 parent 98194fd commit 4fe4e68

File tree

2 files changed

+115
-67
lines changed

2 files changed

+115
-67
lines changed

llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp

Lines changed: 111 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,10 @@ class PointerReplacer {
244244
void replacePointer(Value *V);
245245

246246
private:
247-
bool collectUsersRecursive(Instruction &I);
248247
void replace(Instruction *I);
249-
Value *getReplacement(Value *I);
248+
Value *getReplacement(Value *V) const { return WorkMap.lookup(V); }
250249
bool isAvailable(Instruction *I) const {
251-
return I == &Root || Worklist.contains(I);
250+
return I == &Root || UsersToReplace.contains(I);
252251
}
253252

254253
bool isEqualOrValidAddrSpaceCast(const Instruction *I,
@@ -260,8 +259,7 @@ class PointerReplacer {
260259
return (FromAS == ToAS) || IC.isValidAddrSpaceCast(FromAS, ToAS);
261260
}
262261

263-
SmallPtrSet<Instruction *, 32> ValuesToRevisit;
264-
SmallSetVector<Instruction *, 4> Worklist;
262+
SmallSetVector<Instruction *, 4> UsersToReplace;
265263
MapVector<Value *, Value *> WorkMap;
266264
InstCombinerImpl &IC;
267265
Instruction &Root;
@@ -270,80 +268,131 @@ class PointerReplacer {
270268
} // end anonymous namespace
271269

272270
bool PointerReplacer::collectUsers() {
273-
if (!collectUsersRecursive(Root))
274-
return false;
275-
276-
// Ensure that all outstanding (indirect) users of I
277-
// are inserted into the Worklist. Return false
278-
// otherwise.
279-
return llvm::set_is_subset(ValuesToRevisit, Worklist);
280-
}
271+
SmallVector<Instruction *> Worklist;
272+
SmallSetVector<Instruction *, 4> ValuesToRevisit;
273+
274+
auto PushUsersToWorklist = [&](Instruction *Inst) {
275+
for (auto *U : Inst->users()) {
276+
if (auto *I = dyn_cast<Instruction>(U)) {
277+
if (!isAvailable(I) && !ValuesToRevisit.contains(I))
278+
Worklist.emplace_back(I);
279+
}
280+
}
281+
};
281282

282-
bool PointerReplacer::collectUsersRecursive(Instruction &I) {
283-
for (auto *U : I.users()) {
284-
auto *Inst = cast<Instruction>(&*U);
283+
PushUsersToWorklist(&Root);
284+
while (!Worklist.empty()) {
285+
auto *Inst = Worklist.pop_back_val();
286+
if (!Inst)
287+
return false;
288+
if (isAvailable(Inst))
289+
continue;
285290
if (auto *Load = dyn_cast<LoadInst>(Inst)) {
286291
if (Load->isVolatile())
287292
return false;
288-
Worklist.insert(Load);
293+
UsersToReplace.insert(Load);
289294
} else if (auto *PHI = dyn_cast<PHINode>(Inst)) {
290295
// All incoming values must be instructions for replacability
291296
if (any_of(PHI->incoming_values(),
292297
[](Value *V) { return !isa<Instruction>(V); }))
293298
return false;
294299

295-
// If at least one incoming value of the PHI is not in Worklist,
296-
// store the PHI for revisiting and skip this iteration of the
297-
// loop.
298-
if (any_of(PHI->incoming_values(), [this](Value *V) {
299-
return !isAvailable(cast<Instruction>(V));
300-
})) {
301-
ValuesToRevisit.insert(Inst);
300+
// If all incoming values are available, mark this PHI as
301+
// replacable and push it's users into the worklist.
302+
if (all_of(PHI->incoming_values(),
303+
[&](Value *V) { return isAvailable(cast<Instruction>(V)); })) {
304+
UsersToReplace.insert(PHI);
305+
PushUsersToWorklist(PHI);
302306
continue;
303307
}
304308

305-
Worklist.insert(PHI);
306-
if (!collectUsersRecursive(*PHI))
307-
return false;
308-
} else if (auto *SI = dyn_cast<SelectInst>(Inst)) {
309-
if (!isa<Instruction>(SI->getTrueValue()) ||
310-
!isa<Instruction>(SI->getFalseValue()))
309+
// Not all incoming values are available. If this PHI was already
310+
// visited prior to this iteration, return false.
311+
if (!ValuesToRevisit.insert(PHI))
311312
return false;
312313

313-
if (!isAvailable(cast<Instruction>(SI->getTrueValue())) ||
314-
!isAvailable(cast<Instruction>(SI->getFalseValue()))) {
315-
ValuesToRevisit.insert(Inst);
316-
continue;
314+
// Push PHI back into the stack, followed by unavailable
315+
// incoming values.
316+
Worklist.emplace_back(PHI);
317+
for (unsigned Idx = 0; Idx < PHI->getNumIncomingValues(); ++Idx) {
318+
auto *IncomingValue = cast<Instruction>(PHI->getIncomingValue(Idx));
319+
if (UsersToReplace.contains(IncomingValue))
320+
continue;
321+
if (!ValuesToRevisit.insert(IncomingValue))
322+
return false;
323+
Worklist.emplace_back(IncomingValue);
317324
}
318-
Worklist.insert(SI);
319-
if (!collectUsersRecursive(*SI))
320-
return false;
321-
} else if (isa<GetElementPtrInst>(Inst)) {
322-
Worklist.insert(Inst);
323-
if (!collectUsersRecursive(*Inst))
325+
} else if (auto *SI = dyn_cast<SelectInst>(Inst)) {
326+
auto *TrueInst = dyn_cast<Instruction>(SI->getTrueValue());
327+
auto *FalseInst = dyn_cast<Instruction>(SI->getFalseValue());
328+
if (!TrueInst || !FalseInst)
324329
return false;
330+
331+
UsersToReplace.insert(SI);
332+
PushUsersToWorklist(SI);
333+
} else if (auto *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
334+
UsersToReplace.insert(GEP);
335+
PushUsersToWorklist(GEP);
325336
} else if (auto *MI = dyn_cast<MemTransferInst>(Inst)) {
326337
if (MI->isVolatile())
327338
return false;
328-
Worklist.insert(Inst);
339+
UsersToReplace.insert(Inst);
329340
} else if (isEqualOrValidAddrSpaceCast(Inst, FromAS)) {
330-
Worklist.insert(Inst);
331-
if (!collectUsersRecursive(*Inst))
332-
return false;
341+
UsersToReplace.insert(Inst);
342+
PushUsersToWorklist(Inst);
333343
} else if (Inst->isLifetimeStartOrEnd()) {
334344
continue;
335345
} else {
336346
// TODO: For arbitrary uses with address space mismatches, should we check
337347
// if we can introduce a valid addrspacecast?
338-
LLVM_DEBUG(dbgs() << "Cannot handle pointer user: " << *U << '\n');
348+
LLVM_DEBUG(dbgs() << "Cannot handle pointer user: " << *Inst << '\n');
339349
return false;
340350
}
341351
}
342352

343-
return true;
353+
return llvm::set_is_subset(ValuesToRevisit, UsersToReplace);
344354
}
345355

346-
Value *PointerReplacer::getReplacement(Value *V) { return WorkMap.lookup(V); }
356+
void PointerReplacer::replacePointer(Value *V) {
357+
#ifndef NDEBUG
358+
auto *PT = cast<PointerType>(Root.getType());
359+
auto *NT = cast<PointerType>(V->getType());
360+
assert(PT != NT && "Invalid usage");
361+
#endif
362+
363+
WorkMap[&Root] = V;
364+
SmallVector<Instruction *> Worklist;
365+
SetVector<Instruction *> PostOrderWorklist;
366+
SmallPtrSet<Instruction *, 4> Visited;
367+
368+
// Perform a postorder traversal of the users of Root.
369+
Worklist.push_back(&Root);
370+
while (!Worklist.empty()) {
371+
Instruction *I = Worklist.back();
372+
373+
// If I has not been processed before, push each of its
374+
// replacable users into the worklist.
375+
if (Visited.insert(I).second) {
376+
for (auto *U : I->users()) {
377+
assert(isa<Instruction>(U) &&
378+
"User should not have been"
379+
" collected as it is not an instruction.");
380+
auto *UserInst = cast<Instruction>(U);
381+
if (UsersToReplace.contains(UserInst))
382+
Worklist.push_back(UserInst);
383+
}
384+
// Otherwise, users of I have already been pushed into
385+
// the PostOrderWorklist. Push I as well.
386+
} else {
387+
PostOrderWorklist.insert(I);
388+
Worklist.pop_back();
389+
}
390+
}
391+
392+
// Replace pointers in reverse-postorder.
393+
for (Instruction *I : llvm::reverse(PostOrderWorklist))
394+
replace(I);
395+
}
347396

348397
void PointerReplacer::replace(Instruction *I) {
349398
if (getReplacement(I))
@@ -365,12 +414,20 @@ void PointerReplacer::replace(Instruction *I) {
365414
// replacement (new value).
366415
WorkMap[NewI] = NewI;
367416
} else if (auto *PHI = dyn_cast<PHINode>(I)) {
368-
Type *NewTy = getReplacement(PHI->getIncomingValue(0))->getType();
369-
auto *NewPHI = PHINode::Create(NewTy, PHI->getNumIncomingValues(),
370-
PHI->getName(), PHI->getIterator());
417+
// Create a new PHI by replacing any incoming value that is a user of the
418+
// root pointer and has a replacement.
419+
SmallVector<Value *, 4> IncomingValues;
420+
for (unsigned int I = 0; I < PHI->getNumIncomingValues(); ++I) {
421+
Value *V = getReplacement(PHI->getIncomingValue(I));
422+
if (!V)
423+
V = PHI->getIncomingValue(I);
424+
IncomingValues.push_back(V);
425+
}
426+
auto *NewPHI = PHINode::Create(IncomingValues[0]->getType(),
427+
PHI->getNumIncomingValues(),
428+
PHI->getName() + ".r", PHI->getIterator());
371429
for (unsigned int I = 0; I < PHI->getNumIncomingValues(); ++I)
372-
NewPHI->addIncoming(getReplacement(PHI->getIncomingValue(I)),
373-
PHI->getIncomingBlock(I));
430+
NewPHI->addIncoming(IncomingValues[I], PHI->getIncomingBlock(I));
374431
WorkMap[PHI] = NewPHI;
375432
} else if (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
376433
auto *V = getReplacement(GEP->getPointerOperand());
@@ -431,22 +488,12 @@ void PointerReplacer::replace(Instruction *I) {
431488
}
432489

433490
} else {
491+
dbgs() << "Instruction " << *I
492+
<< " is not supported in PointerReplacer::replace\n";
434493
llvm_unreachable("should never reach here");
435494
}
436495
}
437496

438-
void PointerReplacer::replacePointer(Value *V) {
439-
#ifndef NDEBUG
440-
auto *PT = cast<PointerType>(Root.getType());
441-
auto *NT = cast<PointerType>(V->getType());
442-
assert(PT != NT && "Invalid usage");
443-
#endif
444-
WorkMap[&Root] = V;
445-
446-
for (Instruction *Workitem : Worklist)
447-
replace(Workitem);
448-
}
449-
450497
Instruction *InstCombinerImpl::visitAllocaInst(AllocaInst &AI) {
451498
if (auto *I = simplifyAllocaArraySize(*this, AI, DT))
452499
return I;

llvm/test/Transforms/InstCombine/AMDGPU/ptr-replace-alloca.ll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
%struct.type = type { [256 x <2 x i64>] }
55
@g1 = external hidden addrspace(3) global %struct.type, align 16
66

7+
; This test requires the PtrReplacer to replace users in an RPO traversal.
8+
; Furthermore, %ptr.else need not to be replaced so it must be retained in
9+
; %ptr.sink.
710
define <2 x i64> @func(ptr addrspace(4) byref(%struct.type) align 16 %0, i1 %cmp.0) {
811
; CHECK-LABEL: define <2 x i64> @func(
912
; CHECK-SAME: ptr addrspace(4) byref([[STRUCT_TYPE:%.*]]) align 16 [[TMP0:%.*]], i1 [[CMP_0:%.*]]) {
1013
; CHECK-NEXT: [[ENTRY:.*:]]
11-
; CHECK-NEXT: [[COERCE:%.*]] = alloca [[STRUCT_TYPE]], align 16, addrspace(5)
12-
; CHECK-NEXT: call void @llvm.memcpy.p5.p4.i64(ptr addrspace(5) noundef align 16 dereferenceable(4096) [[COERCE]], ptr addrspace(4) noundef align 16 dereferenceable(4096) [[TMP0]], i64 4096, i1 false)
1314
; CHECK-NEXT: br i1 [[CMP_0]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
1415
; CHECK: [[IF_THEN]]:
15-
; CHECK-NEXT: [[VAL_THEN:%.*]] = addrspacecast ptr addrspace(5) [[COERCE]] to ptr
16+
; CHECK-NEXT: [[VAL_THEN:%.*]] = addrspacecast ptr addrspace(4) [[TMP0]] to ptr
1617
; CHECK-NEXT: br label %[[SINK:.*]]
1718
; CHECK: [[IF_ELSE]]:
1819
; CHECK-NEXT: [[PTR_ELSE:%.*]] = load ptr, ptr addrspace(3) getelementptr inbounds nuw (i8, ptr addrspace(3) @g1, i32 32), align 16

0 commit comments

Comments
 (0)