forked from OSchip/llvm-project
[InstCombine] Transform strrchr to memrchr for constant strings
Add an emitter for the memrchr common extension and simplify the strrchr call handler to use it. This enables transforming calls with the empty string to the test C ? S : 0. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D128954
This commit is contained in:
parent
554aea52d7
commit
0d68ff87d2
|
@ -139,6 +139,10 @@ namespace llvm {
|
||||||
Value *emitMemChr(Value *Ptr, Value *Val, Value *Len, IRBuilderBase &B,
|
Value *emitMemChr(Value *Ptr, Value *Val, Value *Len, IRBuilderBase &B,
|
||||||
const DataLayout &DL, const TargetLibraryInfo *TLI);
|
const DataLayout &DL, const TargetLibraryInfo *TLI);
|
||||||
|
|
||||||
|
/// Emit a call to the memrchr function, analogously to emitMemChr.
|
||||||
|
Value *emitMemRChr(Value *Ptr, Value *Val, Value *Len, IRBuilderBase &B,
|
||||||
|
const DataLayout &DL, const TargetLibraryInfo *TLI);
|
||||||
|
|
||||||
/// Emit a call to the memcmp function.
|
/// Emit a call to the memcmp function.
|
||||||
Value *emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilderBase &B,
|
Value *emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilderBase &B,
|
||||||
const DataLayout &DL, const TargetLibraryInfo *TLI);
|
const DataLayout &DL, const TargetLibraryInfo *TLI);
|
||||||
|
|
|
@ -1305,6 +1305,7 @@ FunctionCallee llvm::getOrInsertLibFunc(Module *M, const TargetLibraryInfo &TLI,
|
||||||
case LibFunc_ldexpf:
|
case LibFunc_ldexpf:
|
||||||
case LibFunc_ldexpl:
|
case LibFunc_ldexpl:
|
||||||
case LibFunc_memchr:
|
case LibFunc_memchr:
|
||||||
|
case LibFunc_memrchr:
|
||||||
case LibFunc_strchr:
|
case LibFunc_strchr:
|
||||||
setArgExtAttr(*F, 1, TLI);
|
setArgExtAttr(*F, 1, TLI);
|
||||||
break;
|
break;
|
||||||
|
@ -1538,6 +1539,15 @@ Value *llvm::emitMemChr(Value *Ptr, Value *Val, Value *Len, IRBuilderBase &B,
|
||||||
{castToCStr(Ptr, B), Val, Len}, B, TLI);
|
{castToCStr(Ptr, B), Val, Len}, B, TLI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value *llvm::emitMemRChr(Value *Ptr, Value *Val, Value *Len, IRBuilderBase &B,
|
||||||
|
const DataLayout &DL, const TargetLibraryInfo *TLI) {
|
||||||
|
LLVMContext &Context = B.GetInsertBlock()->getContext();
|
||||||
|
return emitLibCall(
|
||||||
|
LibFunc_memrchr, B.getInt8PtrTy(),
|
||||||
|
{B.getInt8PtrTy(), B.getInt32Ty(), DL.getIntPtrType(Context)},
|
||||||
|
{castToCStr(Ptr, B), Val, Len}, B, TLI);
|
||||||
|
}
|
||||||
|
|
||||||
Value *llvm::emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilderBase &B,
|
Value *llvm::emitMemCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilderBase &B,
|
||||||
const DataLayout &DL, const TargetLibraryInfo *TLI) {
|
const DataLayout &DL, const TargetLibraryInfo *TLI) {
|
||||||
LLVMContext &Context = B.GetInsertBlock()->getContext();
|
LLVMContext &Context = B.GetInsertBlock()->getContext();
|
||||||
|
|
|
@ -344,30 +344,24 @@ Value *LibCallSimplifier::optimizeStrChr(CallInst *CI, IRBuilderBase &B) {
|
||||||
|
|
||||||
Value *LibCallSimplifier::optimizeStrRChr(CallInst *CI, IRBuilderBase &B) {
|
Value *LibCallSimplifier::optimizeStrRChr(CallInst *CI, IRBuilderBase &B) {
|
||||||
Value *SrcStr = CI->getArgOperand(0);
|
Value *SrcStr = CI->getArgOperand(0);
|
||||||
ConstantInt *CharC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
|
Value *CharVal = CI->getArgOperand(1);
|
||||||
|
ConstantInt *CharC = dyn_cast<ConstantInt>(CharVal);
|
||||||
annotateNonNullNoUndefBasedOnAccess(CI, 0);
|
annotateNonNullNoUndefBasedOnAccess(CI, 0);
|
||||||
|
|
||||||
// Cannot fold anything if we're not looking for a constant.
|
|
||||||
if (!CharC)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
StringRef Str;
|
StringRef Str;
|
||||||
if (!getConstantStringInfo(SrcStr, Str)) {
|
if (!getConstantStringInfo(SrcStr, Str)) {
|
||||||
// strrchr(s, 0) -> strchr(s, 0)
|
// strrchr(s, 0) -> strchr(s, 0)
|
||||||
if (CharC->isZero())
|
if (CharC && CharC->isZero())
|
||||||
return copyFlags(*CI, emitStrChr(SrcStr, '\0', B, TLI));
|
return copyFlags(*CI, emitStrChr(SrcStr, '\0', B, TLI));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the offset.
|
// Try to expand strrchr to the memrchr nonstandard extension if it's
|
||||||
size_t I = (0xFF & CharC->getSExtValue()) == 0
|
// available, or simply fail otherwise.
|
||||||
? Str.size()
|
uint64_t NBytes = Str.size() + 1; // Include the terminating nul.
|
||||||
: Str.rfind(CharC->getSExtValue());
|
Type *IntPtrType = DL.getIntPtrType(CI->getContext());
|
||||||
if (I == StringRef::npos) // Didn't find the char. Return null.
|
Value *Size = ConstantInt::get(IntPtrType, NBytes);
|
||||||
return Constant::getNullValue(CI->getType());
|
return copyFlags(*CI, emitMemRChr(SrcStr, CharVal, Size, B, DL, TLI));
|
||||||
|
|
||||||
// strrchr(s+n,c) -> gep(s+n+i,c)
|
|
||||||
return B.CreateInBoundsGEP(B.getInt8Ty(), SrcStr, B.getInt64(I), "strrchr");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value *LibCallSimplifier::optimizeStrCmp(CallInst *CI, IRBuilderBase &B) {
|
Value *LibCallSimplifier::optimizeStrCmp(CallInst *CI, IRBuilderBase &B) {
|
||||||
|
|
|
@ -101,7 +101,7 @@ define void @fold_strncmp_past_end(i32* %pcmp) {
|
||||||
|
|
||||||
define i8* @fold_strrchr_past_end(i32 %c) {
|
define i8* @fold_strrchr_past_end(i32 %c) {
|
||||||
; CHECK-LABEL: @fold_strrchr_past_end(
|
; CHECK-LABEL: @fold_strrchr_past_end(
|
||||||
; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)
|
; CHECK-NEXT: ret i8* null
|
||||||
;
|
;
|
||||||
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
|
%p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5
|
||||||
%r = call i8* @strrchr(i8* %p5, i32 0)
|
%r = call i8* @strrchr(i8* %p5, i32 0)
|
||||||
|
|
|
@ -58,10 +58,10 @@ define void @test_simplify4() {
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @test_nosimplify1(i32 %chr) {
|
define void @test_xform_to_memrchr(i32 %chr) {
|
||||||
; CHECK-LABEL: @test_nosimplify1(
|
; CHECK-LABEL: @test_xform_to_memrchr(
|
||||||
; CHECK-NEXT: [[DST:%.*]] = call i8* @strrchr(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([14 x i8], [14 x i8]* @hello, i32 0, i32 0), i32 [[CHR:%.*]])
|
; CHECK-NEXT: [[MEMRCHR:%.*]] = call i8* @memrchr(i8* noundef nonnull dereferenceable(14) getelementptr inbounds ([14 x i8], [14 x i8]* @hello, i32 0, i32 0), i32 [[CHR:%.*]], i32 14)
|
||||||
; CHECK-NEXT: store i8* [[DST]], i8** @chp, align 4
|
; CHECK-NEXT: store i8* [[MEMRCHR]], i8** @chp, align 4
|
||||||
; CHECK-NEXT: ret void
|
; CHECK-NEXT: ret void
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||||
|
; Verify the strrchr("", c) to (unsigned char)c ? "" : 0 transformetion.
|
||||||
|
;
|
||||||
|
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
|
||||||
|
|
||||||
|
@s10 = constant [11 x i8] c"0123456789\00"
|
||||||
|
|
||||||
|
declare i8* @strrchr(i8*, i32)
|
||||||
|
|
||||||
|
; Fold strrchr(s + 10, c) to (unsigned char)c ? 0 : s + 10.
|
||||||
|
|
||||||
|
define i8* @fold_strrchr_sp10_x(i32 %c) {
|
||||||
|
; CHECK-LABEL: @fold_strrchr_sp10_x(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8
|
||||||
|
; CHECK-NEXT: [[MEMRCHR_CHAR0CMP:%.*]] = icmp eq i8 [[TMP1]], 0
|
||||||
|
; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[MEMRCHR_CHAR0CMP]], i8* getelementptr inbounds ([11 x i8], [11 x i8]* @s10, i64 0, i64 10), i8* null
|
||||||
|
; CHECK-NEXT: ret i8* [[MEMRCHR_SEL]]
|
||||||
|
;
|
||||||
|
%psp10 = getelementptr [11 x i8], [11 x i8]* @s10, i32 0, i32 10
|
||||||
|
%pc = call i8* @strrchr(i8* %psp10, i32 %c)
|
||||||
|
ret i8* %pc
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; Transform strrchr(s + 9, c) to [the equivalent of] memrchr(s + 9, c, 2).
|
||||||
|
|
||||||
|
define i8* @call_strrchr_sp9_x(i32 %c) {
|
||||||
|
; CHECK-LABEL: @call_strrchr_sp9_x(
|
||||||
|
; CHECK-NEXT: [[MEMRCHR:%.*]] = call i8* @memrchr(i8* noundef nonnull dereferenceable(2) getelementptr inbounds ([11 x i8], [11 x i8]* @s10, i64 0, i64 9), i32 [[C:%.*]], i64 2)
|
||||||
|
; CHECK-NEXT: ret i8* [[MEMRCHR]]
|
||||||
|
;
|
||||||
|
%psp9 = getelementptr [11 x i8], [11 x i8]* @s10, i32 0, i32 9
|
||||||
|
%pc = call i8* @strrchr(i8* %psp9, i32 %c)
|
||||||
|
ret i8* %pc
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; Do not transform strrchr(s + 2, c) (for short strings this could be
|
||||||
|
; folded into a chain of OR expressions ala D128011).
|
||||||
|
|
||||||
|
define i8* @call_strrchr_sp2_x(i32 %c) {
|
||||||
|
; CHECK-LABEL: @call_strrchr_sp2_x(
|
||||||
|
; CHECK-NEXT: [[MEMRCHR:%.*]] = call i8* @memrchr(i8* noundef nonnull dereferenceable(9) getelementptr inbounds ([11 x i8], [11 x i8]* @s10, i64 0, i64 2), i32 [[C:%.*]], i64 9)
|
||||||
|
; CHECK-NEXT: ret i8* [[MEMRCHR]]
|
||||||
|
;
|
||||||
|
%psp2 = getelementptr [11 x i8], [11 x i8]* @s10, i32 0, i32 2
|
||||||
|
%pc = call i8* @strrchr(i8* %psp2, i32 %c)
|
||||||
|
ret i8* %pc
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; Do not transform strrchr(s + 1, c).
|
||||||
|
|
||||||
|
define i8* @call_strrchr_sp1_x(i32 %c) {
|
||||||
|
; CHECK-LABEL: @call_strrchr_sp1_x(
|
||||||
|
; CHECK-NEXT: [[MEMRCHR:%.*]] = call i8* @memrchr(i8* noundef nonnull dereferenceable(10) getelementptr inbounds ([11 x i8], [11 x i8]* @s10, i64 0, i64 1), i32 [[C:%.*]], i64 10)
|
||||||
|
; CHECK-NEXT: ret i8* [[MEMRCHR]]
|
||||||
|
;
|
||||||
|
%psp1 = getelementptr [11 x i8], [11 x i8]* @s10, i32 0, i32 1
|
||||||
|
%pc = call i8* @strrchr(i8* %psp1, i32 %c)
|
||||||
|
ret i8* %pc
|
||||||
|
}
|
Loading…
Reference in New Issue