Index: include/llvm/Transforms/Utils/SimplifyLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -150,6 +150,8 @@ Value *optimizeIsDigit(CallInst *CI, IRBuilder<> &B); Value *optimizeIsAscii(CallInst *CI, IRBuilder<> &B); Value *optimizeToAscii(CallInst *CI, IRBuilder<> &B); + Value *optimizeAtoi(CallInst *CI, IRBuilder<> &B); + Value *optimizeStrtol(CallInst *CI, IRBuilder<> &B); // Formatting and IO Library Call Optimizations Value *optimizeErrorReporting(CallInst *CI, IRBuilder<> &B, Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -102,6 +102,12 @@ }); } +/// Return true if locale is set to C locale +static bool isCLocale(void) { + std::locale Loc; + return Loc.name() == "C"; +} + //===----------------------------------------------------------------------===// // String and Memory Library Call Optimizations //===----------------------------------------------------------------------===// @@ -1677,6 +1683,68 @@ ConstantInt::get(CI->getType(), 0x7F)); } +Value *LibCallSimplifier::optimizeAtoi(CallInst *CI, IRBuilder<> &B) { + StringRef Str; + if (!getConstantStringInfo(CI->getArgOperand(0), Str)) + return nullptr; + + if (!isCLocale()) + return nullptr; + + char *End; + std::string nptr = Str.str(); + errno = 0; + long long int Result = strtoll(nptr.c_str(), &End, 10); + if (errno) + return nullptr; + + if (*End != '\0') + return nullptr; + + if (!isIntN(CI->getType()->getPrimitiveSizeInBits(), Result)) + return nullptr; + + return ConstantInt::get(CI->getType(), Result); +} + +Value *LibCallSimplifier::optimizeStrtol(CallInst *CI, IRBuilder<> &B) { + StringRef Str; + if (!getConstantStringInfo(CI->getArgOperand(0), Str)) + return nullptr; + + if (!isa(CI->getArgOperand(1))) + return nullptr; + + if (!isCLocale()) + return nullptr; + + if (ConstantInt *CInt = dyn_cast(CI->getArgOperand(2))) { + auto Base = CInt->getSExtValue(); + // check base, valid one is between 2 and 36 + if (Base < 2 || Base > 36) + // handle special zero base + if (Base != 0) + return nullptr; + + char *End; + std::string nptr = Str.str(); + errno = 0; + long long int Result = strtoll(nptr.c_str(), &End, Base); + if (errno) + return nullptr; + + if (*End != '\0') + return nullptr; + + if (!isIntN(CI->getType()->getPrimitiveSizeInBits(), Result)) + return nullptr; + + return ConstantInt::get(CI->getType(), Result); + } + + return nullptr; +} + //===----------------------------------------------------------------------===// // Formatting and IO Library Call Optimizations //===----------------------------------------------------------------------===// @@ -2271,6 +2339,13 @@ return optimizeIsAscii(CI, Builder); case LibFunc_toascii: return optimizeToAscii(CI, Builder); + case LibFunc_atoi: + case LibFunc_atol: + case LibFunc_atoll: + return optimizeAtoi(CI, Builder); + case LibFunc_strtol: + case LibFunc_strtoll: + return optimizeStrtol(CI, Builder); case LibFunc_printf: return optimizePrintF(CI, Builder); case LibFunc_sprintf: Index: test/Transforms/InstCombine/str-int-2.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/str-int-2.ll @@ -0,0 +1,109 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +@.str = private unnamed_addr constant [3 x i8] c"12\00", align 1 +@.str.1 = private unnamed_addr constant [2 x i8] c"0\00", align 1 +@.str.2 = private unnamed_addr constant [11 x i8] c"4294967296\00", align 1 +@.str.3 = private unnamed_addr constant [24 x i8] c"10000000000000000000000\00", align 1 +@.str.4 = private unnamed_addr constant [20 x i8] c"9923372036854775807\00", align 1 + +declare i32 @strtol(i8*, i8**, i32) #1 +declare i32 @atoi(i8*) #2 + +define i32 @strtol_dec() #0 { +; CHECK-LABEL: @strtol_dec( +; CHECK-NEXT: ret i32 12 +; + %call = call i32 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 10) #2 + ret i32 %call +} + +define i32 @strtol_base_zero() #0 { +; CHECK-LABEL: @strtol_base_zero( +; CHECK-NEXT: ret i32 12 +; + %call = call i32 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 0) #2 + ret i32 %call +} + +define i32 @strtol_hex() #0 { +; CHECK-LABEL: @strtol_hex( +; CHECK-NEXT: ret i32 18 +; + %call = call i32 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 16) #2 + ret i32 %call +} + +define i32 @strtol_endptr_not_null() #0 { +; 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: 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 + ret i32 %call +} + +define i32 @atoi_test() #0 { +; CHECK-LABEL: @atoi_test( +; CHECK-NEXT: ret i32 12 +; + %call = call i32 @atoi(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0)) #4 + ret i32 %call +} + +define i32 @strtol_not_const_str(i8* %s) #0 { +; CHECK-LABEL: @strtol_not_const_str( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* nocapture [[S:%.*]], i8** null, i32 10) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @strtol(i8* %s, i8** null, i32 10) #3 + ret i32 %call +} + +define i32 @atoi_not_const_str(i8* %s) #0 { +; CHECK-LABEL: @atoi_not_const_str( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @atoi(i8* [[S:%.*]]) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @atoi(i8* %s) #4 + ret i32 %call +} + +define i32 @strtol_not_const_base(i32 %b) #0 { +; CHECK-LABEL: @strtol_not_const_base( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* nocapture getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i8** null, i32 [[B:%.*]]) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 %b) #2 + ret i32 %call +} + +define i32 @strtol_long_int() #0 { +; CHECK-LABEL: @strtol_long_int( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* nocapture getelementptr inbounds ([11 x i8], [11 x i8]* @.str.2, i64 0, i64 0), i8** null, i32 10) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @strtol(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.2, i32 0, i32 0), i8** null, i32 10) #3 + ret i32 %call +} + + +define i32 @strtol_big_overflow() #0 { +; CHECK-LABEL: @strtol_big_overflow( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* nocapture getelementptr inbounds ([24 x i8], [24 x i8]* @.str.3, i64 0, i64 0), i8** null, i32 10) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @strtol(i8* nocapture getelementptr inbounds ([24 x i8], [24 x i8]* @.str.3, i32 0, i32 0), i8** null, i32 10) #2 + ret i32 %call +} + +define i32 @strtol_small_overflow() #0 { +; CHECK-LABEL: @strtol_small_overflow( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strtol(i8* nocapture getelementptr inbounds ([20 x i8], [20 x i8]* @.str.4, i64 0, i64 0), i8** null, i32 10) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @strtol(i8* nocapture getelementptr inbounds ([20 x i8], [20 x i8]* @.str.4, i32 0, i32 0), i8** null, i32 10) #2 + ret i32 %call +} Index: test/Transforms/InstCombine/str-int.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/str-int.ll @@ -0,0 +1,109 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +@.str = private unnamed_addr constant [3 x i8] c"12\00", align 1 +@.str.1 = private unnamed_addr constant [2 x i8] c"0\00", align 1 +@.str.2 = private unnamed_addr constant [11 x i8] c"4294967296\00", align 1 +@.str.3 = private unnamed_addr constant [24 x i8] c"10000000000000000000000\00", align 1 +@.str.4 = private unnamed_addr constant [20 x i8] c"9923372036854775807\00", align 1 +@.str.5 = private unnamed_addr constant [11 x i8] c"4994967295\00", align 1 + +declare i64 @strtol(i8*, i8**, i32) #1 +declare i32 @atoi(i8*) #2 +declare i64 @atoll(i8*) #3 + +define i64 @strtol_dec() #0 { +; CHECK-LABEL: @strtol_dec( +; CHECK-NEXT: ret i64 12 +; + %call = call i64 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 10) #2 + ret i64 %call +} + +define i64 @strtol_base_zero() #0 { +; CHECK-LABEL: @strtol_base_zero( +; CHECK-NEXT: ret i64 12 +; + %call = call i64 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 0) #2 + ret i64 %call +} + +define i64 @strtol_hex() #0 { +; CHECK-LABEL: @strtol_hex( +; CHECK-NEXT: ret i64 18 +; + %call = call i64 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 16) #2 + ret i64 %call +} + +define i64 @strtol_endptr_not_null() #0 { +; 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: 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 + ret i64 %call +} + +define i32 @atoi_test() #0 { +; CHECK-LABEL: @atoi_test( +; CHECK-NEXT: ret i32 12 +; + %call = call i32 @atoi(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0)) #4 + ret i32 %call +} + +define i64 @strtol_not_const_str(i8* %s) #0 { +; CHECK-LABEL: @strtol_not_const_str( +; CHECK-NEXT: [[CALL:%.*]] = call i64 @strtol(i8* nocapture [[S:%.*]], i8** null, i32 10) +; CHECK-NEXT: ret i64 [[CALL]] +; + %call = call i64 @strtol(i8* %s, i8** null, i32 10) #3 + ret i64 %call +} + +define i32 @atoi_not_const_str(i8* %s) #0 { +; CHECK-LABEL: @atoi_not_const_str( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @atoi(i8* [[S:%.*]]) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @atoi(i8* %s) #4 + ret i32 %call +} + +define i64 @strtol_not_const_base(i32 %b) #0 { +; CHECK-LABEL: @strtol_not_const_base( +; CHECK-NEXT: [[CALL:%.*]] = call i64 @strtol(i8* nocapture getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i8** null, i32 [[B:%.*]]) +; CHECK-NEXT: ret i64 [[CALL]] +; + %call = call i64 @strtol(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8** null, i32 %b) #2 + ret i64 %call +} + +define i64 @strtol_long_int() #0 { +; CHECK-LABEL: @strtol_long_int( +; CHECK-NEXT: ret i64 4294967296 +; + %call = call i64 @strtol(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.2, i32 0, i32 0), i8** null, i32 10) #3 + ret i64 %call +} + + +define i64 @strtol_big_overflow() #0 { +; CHECK-LABEL: @strtol_big_overflow( +; CHECK-NEXT: [[CALL:%.*]] = call i64 @strtol(i8* nocapture getelementptr inbounds ([24 x i8], [24 x i8]* @.str.3, i64 0, i64 0), i8** null, i32 10) +; CHECK-NEXT: ret i64 [[CALL]] +; + %call = call i64 @strtol(i8* nocapture getelementptr inbounds ([24 x i8], [24 x i8]* @.str.3, i64 0, i64 0), i8** null, i32 10) #2 + ret i64 %call +} + +define i64 @atoll_test() #0 { +; CHECK-LABEL: @atoll_test( +; CHECK-NEXT: ret i64 4994967295 +; + %call = call i64 @atoll(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.5, i32 0, i32 0)) #3 + ret i64 %call +}