diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -75,7 +75,8 @@ }); } -static Value *convertStrToNumber(CallInst *CI, StringRef &Str, int64_t Base) { +static Value *convertStrToNumber(CallInst *CI, StringRef &Str, Value *EndPtr, + int64_t Base, IRBuilderBase &B) { if (Base < 2 || Base > 36) // handle special zero base if (Base != 0) @@ -97,6 +98,15 @@ if (!isIntN(CI->getType()->getPrimitiveSizeInBits(), Result)) return nullptr; + if (EndPtr) { + // Store the pointer to the end. + uint64_t ILen = End - nptr.c_str(); + Value *Off = B.getInt64(ILen); + Value *StrBeg = CI->getArgOperand(0); + Value *StrEnd = B.CreateInBoundsGEP(B.getInt8Ty(), StrBeg, Off, "endptr"); + B.CreateStore(StrEnd, EndPtr); + } + return ConstantInt::get(CI->getType(), Result); } @@ -2523,7 +2533,7 @@ if (!getConstantStringInfo(CI->getArgOperand(0), Str)) return nullptr; - return convertStrToNumber(CI, Str, 10); + return convertStrToNumber(CI, Str, nullptr, 10, B); } Value *LibCallSimplifier::optimizeStrtol(CallInst *CI, IRBuilderBase &B) { @@ -2531,11 +2541,14 @@ if (!getConstantStringInfo(CI->getArgOperand(0), Str)) return nullptr; - if (!isa(CI->getArgOperand(1))) + Value *EndPtr = CI->getArgOperand(1); + if (isa(EndPtr)) + EndPtr = nullptr; + else if (!isKnownNonZero(EndPtr, DL)) return nullptr; if (ConstantInt *CInt = dyn_cast(CI->getArgOperand(2))) { - return convertStrToNumber(CI, Str, CInt->getSExtValue()); + return convertStrToNumber(CI, Str, EndPtr, CInt->getSExtValue(), B); } return nullptr; diff --git a/llvm/test/Transforms/InstCombine/str-int-2.ll b/llvm/test/Transforms/InstCombine/str-int-2.ll --- a/llvm/test/Transforms/InstCombine/str-int-2.ll +++ b/llvm/test/Transforms/InstCombine/str-int-2.ll @@ -40,14 +40,25 @@ ret i64 %call } -define i64 @strtol_endptr_not_null() #0 { +; Fold a call to strtol with an endptr known to be nonnull. + +define i64 @strtol_endptr_not_null(i8** nonnull %pend) { ; CHECK-LABEL: @strtol_endptr_not_null( -; CHECK-NEXT: [[END:%.*]] = alloca i8*, align 4 -; CHECK-NEXT: [[CALL:%.*]] = call i64 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0), i8** nonnull [[END]], i32 10) +; CHECK-NEXT: store i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 2), i8** [[PEND:%.*]], align 8 +; CHECK-NEXT: ret i64 12 +; + %call = call i64 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** %pend, i32 10) + ret i64 %call +} + +; Don't fold a call to strtol with an endptr that's not known to be nonnull. + +define i64 @strtol_endptr_maybe_null(i8** %end) { +; CHECK-LABEL: @strtol_endptr_maybe_null( +; CHECK-NEXT: [[CALL:%.*]] = call i64 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0), i8** [[END:%.*]], i32 10) ; CHECK-NEXT: ret i64 [[CALL]] ; - %end = alloca i8*, align 4 - %call = call i64 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i32 0, i32 0), i8** %end, i32 10) #2 + %call = call i64 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i32 0, i32 0), i8** %end, i32 10) ret i64 %call } diff --git a/llvm/test/Transforms/InstCombine/str-int.ll b/llvm/test/Transforms/InstCombine/str-int.ll --- a/llvm/test/Transforms/InstCombine/str-int.ll +++ b/llvm/test/Transforms/InstCombine/str-int.ll @@ -40,14 +40,28 @@ ret i32 %call } -define i32 @strtol_endptr_not_null() #0 { +; Fold a call to strtol with an endptr known to be nonnull (the result +; of pointer increment). + +define i32 @strtol_endptr_not_null(i8** %pend) { ; CHECK-LABEL: @strtol_endptr_not_null( -; CHECK-NEXT: [[END:%.*]] = alloca i8*, align 4 -; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0), i8** nonnull [[END]], i32 10) +; CHECK-NEXT: [[ENDP1:%.*]] = getelementptr inbounds i8*, i8** [[PEND:%.*]], i64 1 +; CHECK-NEXT: store i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 2), i8** [[ENDP1]], align 8 +; CHECK-NEXT: ret i32 12 +; + %endp1 = getelementptr inbounds i8*, i8** %pend, i32 1 + %call = call i32 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** %endp1, i32 10) + ret i32 %call +} + +; Don't fold a call to strtol with an endptr that's not known to be nonnull. + +define i32 @strtol_endptr_maybe_null(i8** %end) { +; CHECK-LABEL: @strtol_endptr_maybe_null( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0), i8** [[END:%.*]], i32 10) ; CHECK-NEXT: ret i32 [[CALL]] ; - %end = alloca i8*, align 4 - %call = call i32 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i32 0, i32 0), i8** %end, i32 10) #2 + %call = call i32 @strtol(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i32 0, i32 0), i8** %end, i32 10) ret i32 %call }