Skip to content

Commit d9fa0de

Browse files
authored
[StructurizeCFG] bug fix in zero cost hoist (#157969)
This fixes a bug where zero cost instruction was hoisted to nearest common dominator but the hoisted instruction's operands didn't dominate the common dominator causing poison values.
1 parent ba576d3 commit d9fa0de

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

llvm/lib/Transforms/Scalar/StructurizeCFG.cpp

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ class StructurizeCFG {
308308

309309
void hoistZeroCostElseBlockPhiValues(BasicBlock *ElseBB, BasicBlock *ThenBB);
310310

311+
bool isHoistableInstruction(Instruction *I, BasicBlock *BB,
312+
BasicBlock *HoistTo);
313+
311314
void orderNodes();
312315

313316
void analyzeLoops(RegionNode *N);
@@ -415,11 +418,21 @@ class StructurizeCFGLegacyPass : public RegionPass {
415418

416419
} // end anonymous namespace
417420

421+
char StructurizeCFGLegacyPass::ID = 0;
422+
423+
INITIALIZE_PASS_BEGIN(StructurizeCFGLegacyPass, "structurizecfg",
424+
"Structurize the CFG", false, false)
425+
INITIALIZE_PASS_DEPENDENCY(UniformityInfoWrapperPass)
426+
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
427+
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass)
428+
INITIALIZE_PASS_END(StructurizeCFGLegacyPass, "structurizecfg",
429+
"Structurize the CFG", false, false)
430+
418431
/// Checks whether an instruction is zero cost instruction and checks if the
419432
/// operands are from different BB. If so, this instruction can be coalesced
420433
/// if its hoisted to predecessor block. So, this returns true.
421-
static bool isHoistableInstruction(Instruction *I, BasicBlock *BB,
422-
const TargetTransformInfo *TTI) {
434+
bool StructurizeCFG::isHoistableInstruction(Instruction *I, BasicBlock *BB,
435+
BasicBlock *HoistTo) {
423436
if (I->getParent() != BB || isa<PHINode>(I))
424437
return false;
425438

@@ -432,27 +445,18 @@ static bool isHoistableInstruction(Instruction *I, BasicBlock *BB,
432445
if (CostVal != 0)
433446
return false;
434447

435-
// Check if any operands are instructions defined in the same block.
448+
// Check if all operands are available at the hoisting destination.
436449
for (auto &Op : I->operands()) {
437450
if (auto *OpI = dyn_cast<Instruction>(Op)) {
438-
if (OpI->getParent() == BB)
451+
// Operand must dominate the hoisting destination.
452+
if (!DT->dominates(OpI->getParent(), HoistTo))
439453
return false;
440454
}
441455
}
442456

443457
return true;
444458
}
445459

446-
char StructurizeCFGLegacyPass::ID = 0;
447-
448-
INITIALIZE_PASS_BEGIN(StructurizeCFGLegacyPass, "structurizecfg",
449-
"Structurize the CFG", false, false)
450-
INITIALIZE_PASS_DEPENDENCY(UniformityInfoWrapperPass)
451-
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
452-
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass)
453-
INITIALIZE_PASS_END(StructurizeCFGLegacyPass, "structurizecfg",
454-
"Structurize the CFG", false, false)
455-
456460
/// Structurization can introduce unnecessary VGPR copies due to register
457461
/// coalescing interference. For example, if the Else block has a zero-cost
458462
/// instruction and the Then block modifies the VGPR value, only one value is
@@ -478,7 +482,7 @@ void StructurizeCFG::hoistZeroCostElseBlockPhiValues(BasicBlock *ElseBB,
478482
for (PHINode &Phi : ElseSucc->phis()) {
479483
Value *ElseVal = Phi.getIncomingValueForBlock(ElseBB);
480484
auto *Inst = dyn_cast<Instruction>(ElseVal);
481-
if (!Inst || !isHoistableInstruction(Inst, ElseBB, TTI))
485+
if (!Inst || !isHoistableInstruction(Inst, ElseBB, CommonDominator))
482486
continue;
483487
Inst->removeFromParent();
484488
Inst->insertInto(CommonDominator, Term->getIterator());

llvm/test/Transforms/StructurizeCFG/hoist-zerocost.ll

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,53 @@ merge:
209209
store i32 %phi, ptr %ptr
210210
ret void
211211
}
212+
213+
define void @test_nested_if_2 (i32 %val,ptr %gep, i1 %cond) {
214+
; CHECK-LABEL: define void @test_nested_if_2(
215+
; CHECK-SAME: i32 [[VAL:%.*]], ptr [[GEP:%.*]], i1 [[COND:%.*]]) {
216+
; CHECK-NEXT: [[ENTRY:.*]]:
217+
; CHECK-NEXT: [[COND_INV:%.*]] = xor i1 [[COND]], true
218+
; CHECK-NEXT: [[T825:%.*]] = icmp eq i32 [[VAL]], 0
219+
; CHECK-NEXT: [[T825_INV:%.*]] = xor i1 [[T825]], true
220+
; CHECK-NEXT: br i1 [[T825]], label %[[IF:.*]], label %[[FLOW:.*]]
221+
; CHECK: [[IF]]:
222+
; CHECK-NEXT: [[LOADED:%.*]] = load [[PAIR:%.*]], ptr [[GEP]], align 4
223+
; CHECK-NEXT: br label %[[FLOW]]
224+
; CHECK: [[FLOW1:.*]]:
225+
; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, %[[ELSE:.*]] ], [ [[TMP2:%.*]], %[[FLOW]] ]
226+
; CHECK-NEXT: br i1 [[TMP0]], label %[[IF_2:.*]], label %[[EXIT:.*]]
227+
; CHECK: [[IF_2]]:
228+
; CHECK-NEXT: [[IF_VALUE:%.*]] = extractvalue [[PAIR]] [[TMP1:%.*]], 0
229+
; CHECK-NEXT: br label %[[EXIT]]
230+
; CHECK: [[FLOW]]:
231+
; CHECK-NEXT: [[TMP1]] = phi [[PAIR]] [ [[LOADED]], %[[IF]] ], [ poison, %[[ENTRY]] ]
232+
; CHECK-NEXT: [[TMP2]] = phi i1 [ true, %[[IF]] ], [ false, %[[ENTRY]] ]
233+
; CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ [[COND_INV]], %[[IF]] ], [ [[T825_INV]], %[[ENTRY]] ]
234+
; CHECK-NEXT: br i1 [[TMP3]], label %[[ELSE]], label %[[FLOW1]]
235+
; CHECK: [[ELSE]]:
236+
; CHECK-NEXT: br label %[[FLOW1]]
237+
; CHECK: [[EXIT]]:
238+
; CHECK-NEXT: [[T_SINK168:%.*]] = phi i32 [ 0, %[[FLOW1]] ], [ [[IF_VALUE]], %[[IF_2]] ]
239+
; CHECK-NEXT: store i32 [[T_SINK168]], ptr [[GEP]], align 4
240+
; CHECK-NEXT: ret void
241+
;
242+
entry:
243+
%t825 = icmp eq i32 %val, 0
244+
br i1 %t825, label %if, label %else
245+
246+
if:
247+
%loaded = load %pair, ptr %gep
248+
br i1 %cond, label %if_2, label %else
249+
250+
if_2:
251+
%if_value = extractvalue %pair %loaded, 0
252+
br label %exit
253+
254+
else:
255+
br label %exit
256+
257+
exit:
258+
%phi = phi i32 [ %if_value, %if_2 ], [ 0, %else ]
259+
store i32 %phi,ptr %gep
260+
ret void
261+
}

0 commit comments

Comments
 (0)