Skip to content

Commit 26d0925

Browse files
committed
[Pass, SCCP] Support constant structure in PhiNode
This patch adds support for constant propagation of individual structure members through Phi nodes. Each member is handled independently, allowing optimization opportunities when specific members become constant. Also, update the testcase since we are able to optimize the call parameter now.
1 parent 05a3f76 commit 26d0925

File tree

2 files changed

+118
-43
lines changed

2 files changed

+118
-43
lines changed

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/Analysis/ValueLatticeUtils.h"
2121
#include "llvm/Analysis/ValueTracking.h"
2222
#include "llvm/IR/ConstantRange.h"
23+
#include "llvm/IR/DerivedTypes.h"
2324
#include "llvm/IR/IRBuilder.h"
2425
#include "llvm/IR/InstVisitor.h"
2526
#include "llvm/IR/Instructions.h"
@@ -768,6 +769,7 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
768769
void handleCallArguments(CallBase &CB);
769770
void handleExtractOfWithOverflow(ExtractValueInst &EVI,
770771
const WithOverflowInst *WO, unsigned Idx);
772+
bool isInstUnderDefined(Instruction &Inst);
771773

772774
private:
773775
friend class InstVisitor<SCCPInstVisitor>;
@@ -1383,49 +1385,65 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const {
13831385
// 7. If a conditional branch has a value that is overdefined, make all
13841386
// successors executable.
13851387
void SCCPInstVisitor::visitPHINode(PHINode &PN) {
1386-
// If this PN returns a struct, just mark the result overdefined.
1387-
// TODO: We could do a lot better than this if code actually uses this.
1388-
if (PN.getType()->isStructTy())
1389-
return (void)markOverdefined(&PN);
1390-
1391-
if (getValueState(&PN).isOverdefined())
1392-
return; // Quick exit
1393-
13941388
// Super-extra-high-degree PHI nodes are unlikely to ever be marked constant,
13951389
// and slow us down a lot. Just mark them overdefined.
13961390
if (PN.getNumIncomingValues() > 64)
13971391
return (void)markOverdefined(&PN);
13981392

1399-
unsigned NumActiveIncoming = 0;
1393+
if (isInstUnderDefined(PN))
1394+
return;
1395+
llvm::SmallVector<unsigned> FeasibleIncomingIndices;
1396+
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
1397+
if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent()))
1398+
continue;
1399+
FeasibleIncomingIndices.push_back(i);
1400+
}
14001401

14011402
// Look at all of the executable operands of the PHI node. If any of them
14021403
// are overdefined, the PHI becomes overdefined as well. If they are all
14031404
// constant, and they agree with each other, the PHI becomes the identical
14041405
// constant. If they are constant and don't agree, the PHI is a constant
14051406
// range. If there are no executable operands, the PHI remains unknown.
1406-
ValueLatticeElement PhiState = getValueState(&PN);
1407-
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
1408-
if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent()))
1409-
continue;
1410-
1411-
ValueLatticeElement IV = getValueState(PN.getIncomingValue(i));
1412-
PhiState.mergeIn(IV);
1413-
NumActiveIncoming++;
1414-
if (PhiState.isOverdefined())
1415-
break;
1407+
if (StructType *STy = dyn_cast<StructType>(PN.getType())) {
1408+
for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
1409+
ValueLatticeElement PhiState = getStructValueState(&PN, i);
1410+
if (PhiState.isOverdefined())
1411+
continue;
1412+
for (unsigned j : FeasibleIncomingIndices) {
1413+
ValueLatticeElement IV = getStructValueState(PN.getIncomingValue(j), i);
1414+
PhiState.mergeIn(IV);
1415+
if (PhiState.isOverdefined())
1416+
break;
1417+
}
1418+
mergeInValue(getStructValueState(&PN, i), &PN, PhiState,
1419+
ValueLatticeElement::MergeOptions().setMaxWidenSteps(
1420+
FeasibleIncomingIndices.size() + 1));
1421+
ValueLatticeElement &PhiStateRef = getStructValueState(&PN, i);
1422+
PhiStateRef.setNumRangeExtensions(
1423+
std::max((unsigned)FeasibleIncomingIndices.size(),
1424+
PhiStateRef.getNumRangeExtensions()));
1425+
}
1426+
} else {
1427+
ValueLatticeElement PhiState = getValueState(&PN);
1428+
for (unsigned i : FeasibleIncomingIndices) {
1429+
ValueLatticeElement IV = getValueState(PN.getIncomingValue(i));
1430+
PhiState.mergeIn(IV);
1431+
if (PhiState.isOverdefined())
1432+
break;
1433+
}
1434+
// We allow up to 1 range extension per active incoming value and one
1435+
// additional extension. Note that we manually adjust the number of range
1436+
// extensions to match the number of active incoming values. This helps to
1437+
// limit multiple extensions caused by the same incoming value, if other
1438+
// incoming values are equal.
1439+
mergeInValue(&PN, PhiState,
1440+
ValueLatticeElement::MergeOptions().setMaxWidenSteps(
1441+
FeasibleIncomingIndices.size() + 1));
1442+
ValueLatticeElement &PhiStateRef = getValueState(&PN);
1443+
PhiStateRef.setNumRangeExtensions(
1444+
std::max((unsigned)FeasibleIncomingIndices.size(),
1445+
PhiStateRef.getNumRangeExtensions()));
14161446
}
1417-
1418-
// We allow up to 1 range extension per active incoming value and one
1419-
// additional extension. Note that we manually adjust the number of range
1420-
// extensions to match the number of active incoming values. This helps to
1421-
// limit multiple extensions caused by the same incoming value, if other
1422-
// incoming values are equal.
1423-
mergeInValue(&PN, PhiState,
1424-
ValueLatticeElement::MergeOptions().setMaxWidenSteps(
1425-
NumActiveIncoming + 1));
1426-
ValueLatticeElement &PhiStateRef = getValueState(&PN);
1427-
PhiStateRef.setNumRangeExtensions(
1428-
std::max(NumActiveIncoming, PhiStateRef.getNumRangeExtensions()));
14291447
}
14301448

14311449
void SCCPInstVisitor::visitReturnInst(ReturnInst &I) {
@@ -2125,6 +2143,21 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) {
21252143
}
21262144
}
21272145

2146+
bool SCCPInstVisitor::isInstUnderDefined(Instruction &Inst) {
2147+
// For structure Type, we handle each member seperately.
2148+
// A structure object won't be considered as overDefined when
2149+
// there is at least one member can become constant.
2150+
if (StructType *STy = dyn_cast<StructType>(Inst.getType())) {
2151+
for (unsigned i = 0, e = STy->getNumElements(); i < e; ++i) {
2152+
if (!getStructValueState(&Inst, i).isOverdefined())
2153+
return false;
2154+
}
2155+
return true;
2156+
}
2157+
2158+
return getValueState(&Inst).isOverdefined();
2159+
}
2160+
21282161
void SCCPInstVisitor::solve() {
21292162
// Process the work lists until they are empty!
21302163
while (!BBWorkList.empty() || !InstWorkList.empty()) {

llvm/test/Transforms/SCCP/constant-range-struct.ll

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ true:
2525
br label %exit
2626

2727
false:
28-
%s.3 = insertvalue {i64, i64} undef, i64 30, 0
28+
%s.3 = insertvalue {i64, i64} poison, i64 30, 0
2929
%s.4 = insertvalue {i64, i64} %s.3, i64 300, 1
3030
br label %exit
3131

@@ -39,14 +39,14 @@ define void @struct1_caller() {
3939
; CHECK-NEXT: [[S:%.*]] = call { i64, i64 } @struct1()
4040
; CHECK-NEXT: [[V1:%.*]] = extractvalue { i64, i64 } [[S]], 0
4141
; CHECK-NEXT: [[V2:%.*]] = extractvalue { i64, i64 } [[S]], 1
42-
; CHECK-NEXT: [[T_1:%.*]] = icmp ne i64 [[V1]], 10
43-
; CHECK-NEXT: call void @use(i1 [[T_1]])
44-
; CHECK-NEXT: [[T_2:%.*]] = icmp ult i64 [[V1]], 100
45-
; CHECK-NEXT: call void @use(i1 [[T_2]])
46-
; CHECK-NEXT: [[T_3:%.*]] = icmp ne i64 [[V2]], 0
42+
; CHECK-NEXT: call void @use(i1 true)
43+
; CHECK-NEXT: call void @use(i1 true)
44+
; CHECK-NEXT: [[T_3:%.*]] = icmp eq i64 [[V1]], 20
4745
; CHECK-NEXT: call void @use(i1 [[T_3]])
48-
; CHECK-NEXT: [[T_4:%.*]] = icmp ult i64 [[V2]], 301
49-
; CHECK-NEXT: call void @use(i1 [[T_4]])
46+
; CHECK-NEXT: call void @use(i1 true)
47+
; CHECK-NEXT: call void @use(i1 true)
48+
; CHECK-NEXT: [[T_6:%.*]] = icmp eq i64 [[V2]], 300
49+
; CHECK-NEXT: call void @use(i1 [[T_6]])
5050
; CHECK-NEXT: ret void
5151
;
5252
%s = call {i64, i64} @struct1()
@@ -57,10 +57,14 @@ define void @struct1_caller() {
5757
call void @use(i1 %t.1)
5858
%t.2 = icmp ult i64 %v1, 100
5959
call void @use(i1 %t.2)
60-
%t.3 = icmp ne i64 %v2, 0
60+
%t.3 = icmp eq i64 %v1, 20
6161
call void @use(i1 %t.3)
62-
%t.4 = icmp ult i64 %v2, 301
62+
%t.4 = icmp ne i64 %v2, 0
6363
call void @use(i1 %t.4)
64+
%t.5 = icmp ult i64 %v2, 301
65+
call void @use(i1 %t.5)
66+
%t.6 = icmp eq i64 %v2, 300
67+
call void @use(i1 %t.6)
6468

6569
ret void
6670
}
@@ -76,7 +80,7 @@ define internal {i64, i64} @struct2() {
7680
; CHECK: exit:
7781
; CHECK-NEXT: [[V1:%.*]] = phi i64 [ 20, [[TRUE]] ], [ 30, [[FALSE]] ]
7882
; CHECK-NEXT: [[V2:%.*]] = phi i64 [ 200, [[TRUE]] ], [ 300, [[FALSE]] ]
79-
; CHECK-NEXT: [[S_1:%.*]] = insertvalue { i64, i64 } undef, i64 [[V1]], 0
83+
; CHECK-NEXT: [[S_1:%.*]] = insertvalue { i64, i64 } poison, i64 [[V1]], 0
8084
; CHECK-NEXT: [[S_2:%.*]] = insertvalue { i64, i64 } [[S_1]], i64 [[V2]], 1
8185
; CHECK-NEXT: ret { i64, i64 } [[S_2]]
8286
;
@@ -92,7 +96,7 @@ false:
9296
exit:
9397
%v1 = phi i64 [ 20, %true ], [ 30, %false ]
9498
%v2 = phi i64 [ 200, %true ], [ 300, %false ]
95-
%s.1 = insertvalue {i64, i64} undef, i64 %v1, 0
99+
%s.1 = insertvalue {i64, i64} poison, i64 %v1, 0
96100
%s.2 = insertvalue {i64, i64} %s.1, i64 %v2, 1
97101
ret {i64, i64} %s.2
98102
}
@@ -153,3 +157,41 @@ define void @struct2_caller() {
153157

154158
ret void
155159
}
160+
161+
%"phi_type" = type {i64, i64}
162+
163+
; Function Attrs: mustprogress
164+
define internal noundef %"phi_type" @test(i32 noundef %input) local_unnamed_addr readnone nounwind {
165+
; CHECK-LABEL: @test(
166+
; CHECK-NEXT: br label [[COND_TRUE_I:%.*]]
167+
; CHECK: cond.true.i:
168+
; CHECK-NEXT: br label [[COND_END_I:%.*]]
169+
; CHECK: cond.end.i:
170+
; CHECK-NEXT: ret [[PHI_TYPE:%.*]] poison
171+
;
172+
%cmp.cond = icmp eq i32 %input, 1
173+
br i1 %cmp.cond, label %cond.true.i, label %cond.false.i
174+
175+
cond.true.i:
176+
%r1.tmp = insertvalue %"phi_type" poison, i64 1, 0
177+
%r1.tmp.2 = insertvalue %"phi_type" %r1.tmp, i64 2, 1
178+
br label %cond.end.i
179+
180+
cond.false.i:
181+
%r2.tmp = insertvalue %"phi_type" poison, i64 3, 0
182+
%r2.tmp.2 = insertvalue %"phi_type" %r2.tmp, i64 4, 1
183+
br label %cond.end.i
184+
185+
cond.end.i:
186+
%retval = phi %"phi_type" [ %r1.tmp.2, %cond.true.i ], [ %r2.tmp.2, %cond.false.i ]
187+
ret %"phi_type" %retval
188+
}
189+
190+
define dso_local noundef %"phi_type" @test2() {
191+
; CHECK-LABEL: @test2(
192+
; CHECK-NEXT: [[CALL_1:%.*]] = tail call fastcc [[PHI_TYPE:%.*]] @[[TEST:[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*]](i32 noundef 1)
193+
; CHECK-NEXT: ret [[PHI_TYPE]] { i64 1, i64 2 }
194+
;
195+
%call.1 = tail call fastcc noundef %"phi_type" @test(i32 noundef 1)
196+
ret %"phi_type" %call.1
197+
}

0 commit comments

Comments
 (0)