Skip to content

Commit 211b938

Browse files
aokblastLukacma
authored andcommitted
[SCCP] Support constant structure in PhiNode (llvm#163713)
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 b940927 commit 211b938

File tree

2 files changed

+118
-43
lines changed

2 files changed

+118
-43
lines changed

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 65 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"
@@ -760,6 +761,7 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
760761
void handleCallArguments(CallBase &CB);
761762
void handleExtractOfWithOverflow(ExtractValueInst &EVI,
762763
const WithOverflowInst *WO, unsigned Idx);
764+
bool isInstFullyOverDefined(Instruction &Inst);
763765

764766
private:
765767
friend class InstVisitor<SCCPInstVisitor>;
@@ -1374,49 +1376,66 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const {
13741376
// 7. If a conditional branch has a value that is overdefined, make all
13751377
// successors executable.
13761378
void SCCPInstVisitor::visitPHINode(PHINode &PN) {
1377-
// If this PN returns a struct, just mark the result overdefined.
1378-
// TODO: We could do a lot better than this if code actually uses this.
1379-
if (PN.getType()->isStructTy())
1380-
return (void)markOverdefined(&PN);
1381-
1382-
if (getValueState(&PN).isOverdefined())
1383-
return; // Quick exit
1384-
13851379
// Super-extra-high-degree PHI nodes are unlikely to ever be marked constant,
13861380
// and slow us down a lot. Just mark them overdefined.
13871381
if (PN.getNumIncomingValues() > 64)
13881382
return (void)markOverdefined(&PN);
13891383

1390-
unsigned NumActiveIncoming = 0;
1384+
if (isInstFullyOverDefined(PN))
1385+
return;
1386+
SmallVector<unsigned> FeasibleIncomingIndices;
1387+
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
1388+
if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent()))
1389+
continue;
1390+
FeasibleIncomingIndices.push_back(i);
1391+
}
13911392

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

14221441
void SCCPInstVisitor::visitReturnInst(ReturnInst &I) {
@@ -2127,6 +2146,21 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) {
21272146
}
21282147
}
21292148

2149+
bool SCCPInstVisitor::isInstFullyOverDefined(Instruction &Inst) {
2150+
// For structure Type, we handle each member separately.
2151+
// A structure object won't be considered as overdefined when
2152+
// there is at least one member that is not overdefined.
2153+
if (StructType *STy = dyn_cast<StructType>(Inst.getType())) {
2154+
for (unsigned i = 0, e = STy->getNumElements(); i < e; ++i) {
2155+
if (!getStructValueState(&Inst, i).isOverdefined())
2156+
return false;
2157+
}
2158+
return true;
2159+
}
2160+
2161+
return getValueState(&Inst).isOverdefined();
2162+
}
2163+
21302164
void SCCPInstVisitor::solve() {
21312165
// Process the work lists until they are empty!
21322166
while (!BBWorkList.empty() || !InstWorkList.empty()) {

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

Lines changed: 53 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,40 @@ define void @struct2_caller() {
153157

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

0 commit comments

Comments
 (0)