Skip to content

Commit 2ebc69a

Browse files
authored
[InstCombine] Add support for GEPs in simplifyNonNullOperand (llvm#128365)
Alive2: https://alive2.llvm.org/ce/z/2KE8zG
1 parent a8fb2d0 commit 2ebc69a

File tree

8 files changed

+160
-13
lines changed

8 files changed

+160
-13
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,8 +3996,12 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) {
39963996
if (V->getType()->isPointerTy()) {
39973997
// Simplify the nonnull operand if the parameter is known to be nonnull.
39983998
// Otherwise, try to infer nonnull for it.
3999-
if (Call.paramHasNonNullAttr(ArgNo, /*AllowUndefOrPoison=*/true)) {
4000-
if (Value *Res = simplifyNonNullOperand(V)) {
3999+
bool HasDereferenceable = Call.getParamDereferenceableBytes(ArgNo) > 0;
4000+
if (Call.paramHasAttr(ArgNo, Attribute::NonNull) ||
4001+
(HasDereferenceable &&
4002+
!NullPointerIsDefined(Call.getFunction(),
4003+
V->getType()->getPointerAddressSpace()))) {
4004+
if (Value *Res = simplifyNonNullOperand(V, HasDereferenceable)) {
40014005
replaceOperand(Call, ArgNo, Res);
40024006
Changed = true;
40034007
}

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,10 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
457457

458458
/// Simplify \p V given that it is known to be non-null.
459459
/// Returns the simplified value if possible, otherwise returns nullptr.
460-
Value *simplifyNonNullOperand(Value *V);
460+
/// If \p HasDereferenceable is true, the simplification will not perform
461+
/// same object checks.
462+
Value *simplifyNonNullOperand(Value *V, bool HasDereferenceable,
463+
unsigned Depth = 0);
461464

462465
public:
463466
/// Create and insert the idiom we use to indicate a block is unreachable

llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -982,8 +982,9 @@ static bool canSimplifyNullLoadOrGEP(LoadInst &LI, Value *Op) {
982982
return false;
983983
}
984984

985-
/// TODO: Recursively simplify nonnull value to handle one-use inbounds GEPs.
986-
Value *InstCombinerImpl::simplifyNonNullOperand(Value *V) {
985+
Value *InstCombinerImpl::simplifyNonNullOperand(Value *V,
986+
bool HasDereferenceable,
987+
unsigned Depth) {
987988
if (auto *Sel = dyn_cast<SelectInst>(V)) {
988989
if (isa<ConstantPointerNull>(Sel->getOperand(1)))
989990
return Sel->getOperand(2);
@@ -992,6 +993,23 @@ Value *InstCombinerImpl::simplifyNonNullOperand(Value *V) {
992993
return Sel->getOperand(1);
993994
}
994995

996+
if (!V->hasOneUse())
997+
return nullptr;
998+
999+
if (Depth == 1)
1000+
return nullptr;
1001+
1002+
if (auto *GEP = dyn_cast<GetElementPtrInst>(V)) {
1003+
if (HasDereferenceable || GEP->isInBounds()) {
1004+
if (auto *Res = simplifyNonNullOperand(GEP->getPointerOperand(),
1005+
HasDereferenceable, Depth + 1)) {
1006+
replaceOperand(*GEP, 0, Res);
1007+
addToWorklist(GEP);
1008+
return nullptr;
1009+
}
1010+
}
1011+
}
1012+
9951013
return nullptr;
9961014
}
9971015

@@ -1076,7 +1094,7 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
10761094
}
10771095

10781096
if (!NullPointerIsDefined(LI.getFunction(), LI.getPointerAddressSpace()))
1079-
if (Value *V = simplifyNonNullOperand(Op))
1097+
if (Value *V = simplifyNonNullOperand(Op, /*HasDereferenceable=*/true))
10801098
return replaceOperand(LI, 0, V);
10811099

10821100
return nullptr;
@@ -1444,7 +1462,7 @@ Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) {
14441462
return eraseInstFromFunction(SI);
14451463

14461464
if (!NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace()))
1447-
if (Value *V = simplifyNonNullOperand(Ptr))
1465+
if (Value *V = simplifyNonNullOperand(Ptr, /*HasDereferenceable=*/true))
14481466
return replaceOperand(SI, 1, V);
14491467

14501468
return nullptr;

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,10 +3593,12 @@ Instruction *InstCombinerImpl::visitReturnInst(ReturnInst &RI) {
35933593
Function *F = RI.getFunction();
35943594
Type *RetTy = RetVal->getType();
35953595
if (RetTy->isPointerTy()) {
3596+
bool HasDereferenceable =
3597+
F->getAttributes().getRetDereferenceableBytes() > 0;
35963598
if (F->hasRetAttribute(Attribute::NonNull) ||
3597-
(F->getAttributes().getRetDereferenceableBytes() > 0 &&
3599+
(HasDereferenceable &&
35983600
!NullPointerIsDefined(F, RetTy->getPointerAddressSpace()))) {
3599-
if (Value *V = simplifyNonNullOperand(RetVal))
3601+
if (Value *V = simplifyNonNullOperand(RetVal, HasDereferenceable))
36003602
return replaceOperand(RI, 0, V);
36013603
}
36023604
}

llvm/test/Transforms/InstCombine/load.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,15 @@ define i4 @test_vector_load_i4_non_byte_sized() {
439439
%res0 = load i4, ptr %ptr0, align 1
440440
ret i4 %res0
441441
}
442+
443+
define i32 @load_select_with_null_gep(i1 %cond, ptr %p, i64 %off) {
444+
; CHECK-LABEL: @load_select_with_null_gep(
445+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[SEL:%.*]], i64 [[OFF:%.*]]
446+
; CHECK-NEXT: [[RES:%.*]] = load i32, ptr [[GEP]], align 4
447+
; CHECK-NEXT: ret i32 [[RES]]
448+
;
449+
%sel = select i1 %cond, ptr %p, ptr null
450+
%gep = getelementptr i8, ptr %sel, i64 %off
451+
%res = load i32, ptr %gep, align 4
452+
ret i32 %res
453+
}

llvm/test/Transforms/InstCombine/nonnull-select.ll

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,102 @@ define void @nonnull_noundef_call2(i1 %cond, ptr %p) {
8686
ret void
8787
}
8888

89+
define void @nonnull_call_gep(i1 %cond, ptr %p, i64 %off) {
90+
; CHECK-LABEL: @nonnull_call_gep(
91+
; CHECK-NEXT: [[PTR:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]]
92+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[PTR]], i64 [[OFF:%.*]]
93+
; CHECK-NEXT: call void @f(ptr nonnull [[GEP]])
94+
; CHECK-NEXT: ret void
95+
;
96+
%ptr = select i1 %cond, ptr null, ptr %p
97+
%gep = getelementptr i8, ptr %ptr, i64 %off
98+
call void @f(ptr nonnull %gep)
99+
ret void
100+
}
101+
102+
define void @nonnull_call_gep_multiuse(i1 %cond, ptr %p, i64 %off) {
103+
; CHECK-LABEL: @nonnull_call_gep_multiuse(
104+
; CHECK-NEXT: [[PTR:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]]
105+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[OFF:%.*]]
106+
; CHECK-NEXT: call void @f(ptr nonnull [[GEP]])
107+
; CHECK-NEXT: call void @f(ptr [[GEP]])
108+
; CHECK-NEXT: ret void
109+
;
110+
%ptr = select i1 %cond, ptr null, ptr %p
111+
%gep = getelementptr inbounds i8, ptr %ptr, i64 %off
112+
call void @f(ptr nonnull %gep)
113+
call void @f(ptr %gep)
114+
ret void
115+
}
116+
117+
define void @all_nonnull_call_gep_multiuse(i1 %cond, ptr %p, i64 %off) {
118+
; CHECK-LABEL: @all_nonnull_call_gep_multiuse(
119+
; CHECK-NEXT: [[PTR:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]]
120+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 [[OFF:%.*]]
121+
; CHECK-NEXT: call void @f(ptr nonnull [[GEP]])
122+
; CHECK-NEXT: call void @f(ptr nonnull [[GEP]])
123+
; CHECK-NEXT: ret void
124+
;
125+
%ptr = select i1 %cond, ptr null, ptr %p
126+
%gep = getelementptr inbounds i8, ptr %ptr, i64 %off
127+
call void @f(ptr nonnull %gep)
128+
call void @f(ptr nonnull %gep)
129+
ret void
130+
}
131+
132+
define void @nonnull_call_gep_inbounds(i1 %cond, ptr %p, i64 %off) {
133+
; CHECK-LABEL: @nonnull_call_gep_inbounds(
134+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[OFF:%.*]]
135+
; CHECK-NEXT: call void @f(ptr nonnull [[GEP]])
136+
; CHECK-NEXT: ret void
137+
;
138+
%ptr = select i1 %cond, ptr null, ptr %p
139+
%gep = getelementptr inbounds i8, ptr %ptr, i64 %off
140+
call void @f(ptr nonnull %gep)
141+
ret void
142+
}
143+
144+
define void @nonnull_dereferenceable_call_gep(i1 %cond, ptr %p, i64 %off) {
145+
; CHECK-LABEL: @nonnull_dereferenceable_call_gep(
146+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[PTR:%.*]], i64 [[OFF:%.*]]
147+
; CHECK-NEXT: call void @f(ptr dereferenceable(1) [[GEP]])
148+
; CHECK-NEXT: ret void
149+
;
150+
%ptr = select i1 %cond, ptr null, ptr %p
151+
%gep = getelementptr i8, ptr %ptr, i64 %off
152+
call void @f(ptr dereferenceable(1) %gep)
153+
ret void
154+
}
155+
156+
define nonnull ptr @nonnull_ret_gep(i1 %cond, ptr %p, i64 %off) {
157+
; CHECK-LABEL: @nonnull_ret_gep(
158+
; CHECK-NEXT: [[PTR:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]]
159+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[PTR]], i64 [[OFF:%.*]]
160+
; CHECK-NEXT: ret ptr [[GEP]]
161+
;
162+
%ptr = select i1 %cond, ptr null, ptr %p
163+
%gep = getelementptr i8, ptr %ptr, i64 %off
164+
ret ptr %gep
165+
}
166+
167+
define nonnull ptr @nonnull_ret_gep_inbounds(i1 %cond, ptr %p, i64 %off) {
168+
; CHECK-LABEL: @nonnull_ret_gep_inbounds(
169+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[PTR:%.*]], i64 [[OFF:%.*]]
170+
; CHECK-NEXT: ret ptr [[GEP]]
171+
;
172+
%ptr = select i1 %cond, ptr null, ptr %p
173+
%gep = getelementptr inbounds i8, ptr %ptr, i64 %off
174+
ret ptr %gep
175+
}
176+
177+
define dereferenceable(1) ptr @nonnull_dereferenceable_ret_gep(i1 %cond, ptr %p, i64 %off) {
178+
; CHECK-LABEL: @nonnull_dereferenceable_ret_gep(
179+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[PTR:%.*]], i64 [[OFF:%.*]]
180+
; CHECK-NEXT: ret ptr [[GEP]]
181+
;
182+
%ptr = select i1 %cond, ptr null, ptr %p
183+
%gep = getelementptr i8, ptr %ptr, i64 %off
184+
ret ptr %gep
185+
}
186+
89187
declare void @f(ptr)

llvm/test/Transforms/InstCombine/store.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,18 @@ define void @store_select_with_unknown(i1 %cond, ptr %p, ptr %p2) {
387387
ret void
388388
}
389389

390+
define void @store_select_with_null_gep(i1 %cond, ptr %p, i64 %off) {
391+
; CHECK-LABEL: @store_select_with_null_gep(
392+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[SEL:%.*]], i64 [[OFF:%.*]]
393+
; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4
394+
; CHECK-NEXT: ret void
395+
;
396+
%sel = select i1 %cond, ptr %p, ptr null
397+
%gep = getelementptr i8, ptr %sel, i64 %off
398+
store i32 0, ptr %gep, align 4
399+
ret void
400+
}
401+
390402
!0 = !{!4, !4, i64 0}
391403
!1 = !{!"omnipotent char", !2}
392404
!2 = !{!"Simple C/C++ TBAA"}

llvm/test/Transforms/PhaseOrdering/memset-combine.ll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
define void @merge_memset(ptr %p, i1 %cond) {
77
; CHECK-LABEL: define void @merge_memset(
88
; CHECK-SAME: ptr [[P:%.*]], i1 [[COND:%.*]]) {
9-
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr null, ptr [[P]]
10-
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(4096) [[P]], i8 0, i64 4096, i1 false)
11-
; CHECK-NEXT: [[OFF:%.*]] = getelementptr inbounds nuw i8, ptr [[SEL]], i64 4096
12-
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(768) [[OFF]], i8 0, i64 768, i1 false)
9+
; CHECK-NEXT: [[OFF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4096
10+
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[P]], i8 0, i64 4864, i1 false)
1311
; CHECK-NEXT: ret void
1412
;
1513
%sel = select i1 %cond, ptr null, ptr %p

0 commit comments

Comments
 (0)