Skip to content

Commit a7df02f

Browse files
authored
[InstCombine] Make strlen optimization more resilient to different gep types. (#153623)
This makes the optimization in optimizeStringLength for strlen(gep @glob, %x) -> sub endof@glob, %x a little more resilient, and maybe a bit more correct for geps with non-array types.
1 parent d286f2e commit a7df02f

File tree

5 files changed

+124
-32
lines changed

5 files changed

+124
-32
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,6 @@ GetPointerBaseWithConstantOffset(const Value *Ptr, int64_t &Offset,
359359
AllowNonInbounds);
360360
}
361361

362-
/// Returns true if the GEP is based on a pointer to a string (array of
363-
// \p CharSize integers) and is indexing into this string.
364-
LLVM_ABI bool isGEPBasedOnPointerToString(const GEPOperator *GEP,
365-
unsigned CharSize = 8);
366-
367362
/// Represents offset+length into a ConstantDataArray.
368363
struct ConstantDataArraySlice {
369364
/// ConstantDataArray pointer. nullptr indicates a zeroinitializer (a valid

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6356,27 +6356,6 @@ llvm::FindInsertedValue(Value *V, ArrayRef<unsigned> idx_range,
63566356
return nullptr;
63576357
}
63586358

6359-
bool llvm::isGEPBasedOnPointerToString(const GEPOperator *GEP,
6360-
unsigned CharSize) {
6361-
// Make sure the GEP has exactly three arguments.
6362-
if (GEP->getNumOperands() != 3)
6363-
return false;
6364-
6365-
// Make sure the index-ee is a pointer to array of \p CharSize integers.
6366-
// CharSize.
6367-
ArrayType *AT = dyn_cast<ArrayType>(GEP->getSourceElementType());
6368-
if (!AT || !AT->getElementType()->isIntegerTy(CharSize))
6369-
return false;
6370-
6371-
// Check to make sure that the first operand of the GEP is an integer and
6372-
// has value 0 so that we are sure we're indexing into the initializer.
6373-
const ConstantInt *FirstIdx = dyn_cast<ConstantInt>(GEP->getOperand(1));
6374-
if (!FirstIdx || !FirstIdx->isZero())
6375-
return false;
6376-
6377-
return true;
6378-
}
6379-
63806359
// If V refers to an initialized global constant, set Slice either to
63816360
// its initializer if the size of its elements equals ElementSize, or,
63826361
// for ElementSize == 8, to its representation as an array of unsiged

llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/Analysis/Loads.h"
2121
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
2222
#include "llvm/Analysis/TargetLibraryInfo.h"
23+
#include "llvm/Analysis/Utils/Local.h"
2324
#include "llvm/Analysis/ValueTracking.h"
2425
#include "llvm/IR/AttributeMask.h"
2526
#include "llvm/IR/DataLayout.h"
@@ -977,8 +978,14 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
977978
// it's not very useful because calling strlen for a pointer of other types is
978979
// very uncommon.
979980
if (GEPOperator *GEP = dyn_cast<GEPOperator>(Src)) {
980-
// TODO: Handle subobjects.
981-
if (!isGEPBasedOnPointerToString(GEP, CharSize))
981+
unsigned BW = DL.getIndexTypeSizeInBits(GEP->getType());
982+
SmallMapVector<Value *, APInt, 4> VarOffsets;
983+
APInt ConstOffset(BW, 0);
984+
assert(CharSize % 8 == 0 && "Expected a multiple of 8 sized CharSize");
985+
// Check the gep is a single variable offset.
986+
if (!GEP->collectOffset(DL, BW, VarOffsets, ConstOffset) ||
987+
VarOffsets.size() != 1 || ConstOffset != 0 ||
988+
VarOffsets.begin()->second != CharSize / 8)
982989
return nullptr;
983990

984991
ConstantDataArraySlice Slice;
@@ -1000,18 +1007,16 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
10001007
return nullptr;
10011008
}
10021009

1003-
Value *Offset = GEP->getOperand(2);
1010+
Value *Offset = VarOffsets.begin()->first;
10041011
KnownBits Known = computeKnownBits(Offset, DL, nullptr, CI, nullptr);
1005-
uint64_t ArrSize =
1006-
cast<ArrayType>(GEP->getSourceElementType())->getNumElements();
10071012

10081013
// If Offset is not provably in the range [0, NullTermIdx], we can still
10091014
// optimize if we can prove that the program has undefined behavior when
10101015
// Offset is outside that range. That is the case when GEP->getOperand(0)
10111016
// is a pointer to an object whose memory extent is NullTermIdx+1.
10121017
if ((Known.isNonNegative() && Known.getMaxValue().ule(NullTermIdx)) ||
10131018
(isa<GlobalVariable>(GEP->getOperand(0)) &&
1014-
NullTermIdx == ArrSize - 1)) {
1019+
NullTermIdx == Slice.Length - 1)) {
10151020
Offset = B.CreateSExtOrTrunc(Offset, CI->getType());
10161021
return B.CreateSub(ConstantInt::get(CI->getType(), NullTermIdx),
10171022
Offset);

llvm/test/Transforms/InstCombine/strlen-1.ll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,25 @@ define i32 @test_simplify10_inbounds(i32 %x) {
116116
ret i32 %hello_l
117117
}
118118

119+
define i32 @test_simplify10_inbounds_i8gep(i32 %x) {
120+
; CHECK-LABEL: @test_simplify10_inbounds_i8gep(
121+
; CHECK-NEXT: [[HELLO_L:%.*]] = sub i32 5, [[X:%.*]]
122+
; CHECK-NEXT: ret i32 [[HELLO_L]]
123+
;
124+
%hello_p = getelementptr inbounds i8, ptr @hello, i32 %x
125+
%hello_l = call i32 @strlen(ptr %hello_p)
126+
ret i32 %hello_l
127+
}
128+
129+
define i32 @test_simplify10_inbounds_i8gep_const() {
130+
; CHECK-LABEL: @test_simplify10_inbounds_i8gep_const(
131+
; CHECK-NEXT: ret i32 3
132+
;
133+
%hello_p = getelementptr inbounds i8, ptr @hello, i32 2
134+
%hello_l = call i32 @strlen(ptr %hello_p)
135+
ret i32 %hello_l
136+
}
137+
119138
define i32 @test_simplify10_no_inbounds(i32 %x) {
120139
; CHECK-LABEL: @test_simplify10_no_inbounds(
121140
; CHECK-NEXT: [[HELLO_L:%.*]] = sub i32 5, [[X:%.*]]
@@ -126,6 +145,16 @@ define i32 @test_simplify10_no_inbounds(i32 %x) {
126145
ret i32 %hello_l
127146
}
128147

148+
define i32 @test_simplify10_no_inbounds_i8gep(i32 %x) {
149+
; CHECK-LABEL: @test_simplify10_no_inbounds_i8gep(
150+
; CHECK-NEXT: [[HELLO_L:%.*]] = sub i32 5, [[X:%.*]]
151+
; CHECK-NEXT: ret i32 [[HELLO_L]]
152+
;
153+
%hello_p = getelementptr i8, ptr @hello, i32 %x
154+
%hello_l = call i32 @strlen(ptr %hello_p)
155+
ret i32 %hello_l
156+
}
157+
129158
; strlen(@null_hello_mid + (x & 7)) --> 9 - (x & 7)
130159

131160
define i32 @test_simplify11(i32 %x) {

llvm/test/Transforms/InstCombine/wcslen-1.ll

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,63 @@ define i64 @test_simplify10(i32 %x) {
118118
ret i64 %hello_l
119119
}
120120

121+
define i64 @test_simplify10_gepi32(i64 %x) {
122+
; CHECK-LABEL: @test_simplify10_gepi32(
123+
; CHECK-NEXT: [[HELLO_L:%.*]] = sub i64 5, [[X:%.*]]
124+
; CHECK-NEXT: ret i64 [[HELLO_L]]
125+
;
126+
%hello_p = getelementptr inbounds i32, ptr @hello, i64 %x
127+
%hello_l = call i64 @wcslen(ptr %hello_p)
128+
ret i64 %hello_l
129+
}
130+
131+
define i64 @test_simplify10_gepi64(i32 %x) {
132+
; CHECK-LABEL: @test_simplify10_gepi64(
133+
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[X:%.*]] to i64
134+
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds i64, ptr @hello, i64 [[TMP1]]
135+
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(ptr nonnull [[HELLO_P]])
136+
; CHECK-NEXT: ret i64 [[HELLO_L]]
137+
;
138+
%hello_p = getelementptr inbounds i64, ptr @hello, i32 %x
139+
%hello_l = call i64 @wcslen(ptr %hello_p)
140+
ret i64 %hello_l
141+
}
142+
143+
define i64 @test_simplify10_gepi16(i64 %x) {
144+
; CHECK-LABEL: @test_simplify10_gepi16(
145+
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds i16, ptr @hello, i64 [[X:%.*]]
146+
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(ptr nonnull [[HELLO_P]])
147+
; CHECK-NEXT: ret i64 [[HELLO_L]]
148+
;
149+
%hello_p = getelementptr inbounds i16, ptr @hello, i64 %x
150+
%hello_l = call i64 @wcslen(ptr %hello_p)
151+
ret i64 %hello_l
152+
}
153+
154+
define i64 @test_simplify10_gepi8(i64 %x) {
155+
; CHECK-LABEL: @test_simplify10_gepi8(
156+
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds i8, ptr @hello, i64 [[TMP1:%.*]]
157+
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(ptr nonnull [[HELLO_P]])
158+
; CHECK-NEXT: ret i64 [[HELLO_L]]
159+
;
160+
%hello_p = getelementptr inbounds i8, ptr @hello, i64 %x
161+
%hello_l = call i64 @wcslen(ptr %hello_p)
162+
ret i64 %hello_l
163+
}
164+
165+
define i64 @test_simplify10_gepi8mul4(i64 %x) {
166+
; CHECK-LABEL: @test_simplify10_gepi8mul4(
167+
; CHECK-NEXT: [[Y:%.*]] = shl i64 [[X:%.*]], 2
168+
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds i8, ptr @hello, i64 [[Y]]
169+
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(ptr nonnull [[HELLO_P]])
170+
; CHECK-NEXT: ret i64 [[HELLO_L]]
171+
;
172+
%y = mul i64 %x, 4
173+
%hello_p = getelementptr inbounds i8, ptr @hello, i64 %y
174+
%hello_l = call i64 @wcslen(ptr %hello_p)
175+
ret i64 %hello_l
176+
}
177+
121178
; wcslen(@null_hello_mid + (x & 7)) --> 9 - (x & 7)
122179

123180
define i64 @test_simplify11(i32 %x) {
@@ -133,6 +190,33 @@ define i64 @test_simplify11(i32 %x) {
133190
ret i64 %hello_l
134191
}
135192

193+
define i64 @test_simplify11_gepi32(i32 %x) {
194+
; CHECK-LABEL: @test_simplify11_gepi32(
195+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 7
196+
; CHECK-NEXT: [[NARROW:%.*]] = sub nuw nsw i32 9, [[AND]]
197+
; CHECK-NEXT: [[HELLO_L:%.*]] = zext nneg i32 [[NARROW]] to i64
198+
; CHECK-NEXT: ret i64 [[HELLO_L]]
199+
;
200+
%and = and i32 %x, 7
201+
%hello_p = getelementptr inbounds i32, ptr @null_hello_mid, i32 %and
202+
%hello_l = call i64 @wcslen(ptr %hello_p)
203+
ret i64 %hello_l
204+
}
205+
206+
define i64 @test_simplify11_gepi8(i32 %x) {
207+
; CHECK-LABEL: @test_simplify11_gepi8(
208+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 7
209+
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i32 [[AND]] to i64
210+
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds nuw i8, ptr @null_hello_mid, i64 [[TMP1]]
211+
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(ptr nonnull [[HELLO_P]])
212+
; CHECK-NEXT: ret i64 [[HELLO_L]]
213+
;
214+
%and = and i32 %x, 7
215+
%hello_p = getelementptr inbounds i8, ptr @null_hello_mid, i32 %and
216+
%hello_l = call i64 @wcslen(ptr %hello_p)
217+
ret i64 %hello_l
218+
}
219+
136220
; Check cases that shouldn't be simplified.
137221

138222
define i64 @test_no_simplify1() {

0 commit comments

Comments
 (0)