Skip to content

Commit 6c7c539

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 6c7c539

File tree

2 files changed

+119
-44
lines changed

2 files changed

+119
-44
lines changed

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 65 additions & 32 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"
@@ -1383,49 +1384,81 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const {
13831384
// 7. If a conditional branch has a value that is overdefined, make all
13841385
// successors executable.
13851386
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-
13941387
// Super-extra-high-degree PHI nodes are unlikely to ever be marked constant,
13951388
// and slow us down a lot. Just mark them overdefined.
13961389
if (PN.getNumIncomingValues() > 64)
13971390
return (void)markOverdefined(&PN);
13981391

1399-
unsigned NumActiveIncoming = 0;
1392+
// For structure Type, we handle each member seperately.
1393+
// A structure object won't be considered as overDefined when
1394+
// there is at least one member can become constant.
1395+
bool IsOverDefined = true;
1396+
if (StructType *STy = dyn_cast<StructType>(PN.getType())) {
1397+
for (unsigned i = 0, e = STy->getNumElements(); i < e; ++i) {
1398+
ValueLatticeElement PNState = getStructValueState(&PN, i);
1399+
IsOverDefined &= PNState.isOverdefined();
1400+
if (!IsOverDefined)
1401+
break;
1402+
}
1403+
} else {
1404+
ValueLatticeElement PhiState = getValueState(&PN);
1405+
IsOverDefined &= PhiState.isOverdefined();
1406+
}
1407+
1408+
if (IsOverDefined)
1409+
return;
1410+
std::vector<unsigned> FeasibleIncomingIndices;
1411+
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
1412+
if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent()))
1413+
continue;
1414+
FeasibleIncomingIndices.push_back(i);
1415+
}
14001416

14011417
// Look at all of the executable operands of the PHI node. If any of them
14021418
// are overdefined, the PHI becomes overdefined as well. If they are all
14031419
// constant, and they agree with each other, the PHI becomes the identical
14041420
// constant. If they are constant and don't agree, the PHI is a constant
14051421
// 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;
1416-
}
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()));
1422+
if (StructType *STy = dyn_cast<StructType>(PN.getType())) {
1423+
for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
1424+
ValueLatticeElement PhiState = getStructValueState(&PN, i);
1425+
if (PhiState.isOverdefined())
1426+
continue;
1427+
for (unsigned j : FeasibleIncomingIndices) {
1428+
ValueLatticeElement IV = getStructValueState(PN.getIncomingValue(j), i);
1429+
PhiState.mergeIn(IV);
1430+
if (PhiState.isOverdefined())
1431+
break;
1432+
}
1433+
mergeInValue(getStructValueState(&PN, i), &PN, PhiState,
1434+
ValueLatticeElement::MergeOptions().setMaxWidenSteps(
1435+
FeasibleIncomingIndices.size() + 1));
1436+
ValueLatticeElement &PhiStateRef = getStructValueState(&PN, i);
1437+
PhiStateRef.setNumRangeExtensions(
1438+
std::max((unsigned)FeasibleIncomingIndices.size(),
1439+
PhiStateRef.getNumRangeExtensions()));
1440+
}
1441+
} else {
1442+
ValueLatticeElement PhiState = getValueState(&PN);
1443+
for (unsigned i : FeasibleIncomingIndices) {
1444+
ValueLatticeElement IV = getValueState(PN.getIncomingValue(i));
1445+
PhiState.mergeIn(IV);
1446+
if (PhiState.isOverdefined())
1447+
break;
1448+
}
1449+
// We allow up to 1 range extension per active incoming value and one
1450+
// additional extension. Note that we manually adjust the number of range
1451+
// extensions to match the number of active incoming values. This helps to
1452+
// limit multiple extensions caused by the same incoming value, if other
1453+
// incoming values are equal.
1454+
mergeInValue(&PN, PhiState,
1455+
ValueLatticeElement::MergeOptions().setMaxWidenSteps(
1456+
FeasibleIncomingIndices.size() + 1));
1457+
ValueLatticeElement &PhiStateRef = getValueState(&PN);
1458+
PhiStateRef.setNumRangeExtensions(
1459+
std::max((unsigned)FeasibleIncomingIndices.size(),
1460+
PhiStateRef.getNumRangeExtensions()));
1461+
}
14291462
}
14301463

14311464
void SCCPInstVisitor::visitReturnInst(ReturnInst &I) {

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)