From 3e3aecbc2cd760b958e38a163f5162f939405f14 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Wed, 5 Oct 2011 22:27:16 +0000 Subject: [PATCH] PR11061: Make simplifylibcalls fold strcmp("", x) correctly. While I'm here, fix the related issue with strncmp, add some actual tests for strcmp and strncmp, and start using StringRef::compare for constant folding instead of using strcmp/strncmp so that the optimized IR isn't dependent on the host's implementation of strcmp. llvm-svn: 141227 --- .../Transforms/Scalar/SimplifyLibCalls.cpp | 31 ++++--- .../Transforms/SimplifyLibCalls/StrCmp.ll | 73 +++++++++++---- .../Transforms/SimplifyLibCalls/StrNCmp.ll | 91 ++++++++++++++----- 3 files changed, 140 insertions(+), 55 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Scalar/SimplifyLibCalls.cpp index e433e30ea824..fbb9465743ce 100644 --- a/llvm/lib/Transforms/Scalar/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Scalar/SimplifyLibCalls.cpp @@ -338,16 +338,17 @@ struct StrCmpOpt : public LibCallOptimization { bool HasStr1 = GetConstantStringInfo(Str1P, Str1); bool HasStr2 = GetConstantStringInfo(Str2P, Str2); - if (HasStr1 && Str1.empty()) // strcmp("", x) -> *x - return B.CreateZExt(B.CreateLoad(Str2P, "strcmpload"), CI->getType()); - - if (HasStr2 && Str2.empty()) // strcmp(x,"") -> *x - return B.CreateZExt(B.CreateLoad(Str1P, "strcmpload"), CI->getType()); - // strcmp(x, y) -> cnst (if both x and y are constant strings) if (HasStr1 && HasStr2) return ConstantInt::get(CI->getType(), - strcmp(Str1.c_str(),Str2.c_str())); + StringRef(Str1).compare(Str2)); + + if (HasStr1 && Str1.empty()) // strcmp("", x) -> -*x + return B.CreateNeg(B.CreateZExt(B.CreateLoad(Str2P, "strcmpload"), + CI->getType())); + + if (HasStr2 && Str2.empty()) // strcmp(x,"") -> *x + return B.CreateZExt(B.CreateLoad(Str1P, "strcmpload"), CI->getType()); // strcmp(P, "x") -> memcmp(P, "x", 2) uint64_t Len1 = GetStringLength(Str1P); @@ -400,16 +401,20 @@ struct StrNCmpOpt : public LibCallOptimization { bool HasStr1 = GetConstantStringInfo(Str1P, Str1); bool HasStr2 = GetConstantStringInfo(Str2P, Str2); - if (HasStr1 && Str1.empty()) // strncmp("", x, n) -> *x - return B.CreateZExt(B.CreateLoad(Str2P, "strcmpload"), CI->getType()); + // strncmp(x, y) -> cnst (if both x and y are constant strings) + if (HasStr1 && HasStr2) { + StringRef SubStr1 = StringRef(Str1).substr(0, Length); + StringRef SubStr2 = StringRef(Str2).substr(0, Length); + return ConstantInt::get(CI->getType(), SubStr1.compare(SubStr2)); + } + + if (HasStr1 && Str1.empty()) // strncmp("", x, n) -> -*x + return B.CreateNeg(B.CreateZExt(B.CreateLoad(Str2P, "strcmpload"), + CI->getType())); if (HasStr2 && Str2.empty()) // strncmp(x, "", n) -> *x return B.CreateZExt(B.CreateLoad(Str1P, "strcmpload"), CI->getType()); - // strncmp(x, y) -> cnst (if both x and y are constant strings) - if (HasStr1 && HasStr2) - return ConstantInt::get(CI->getType(), - strncmp(Str1.c_str(), Str2.c_str(), Length)); return 0; } }; diff --git a/llvm/test/Transforms/SimplifyLibCalls/StrCmp.ll b/llvm/test/Transforms/SimplifyLibCalls/StrCmp.ll index 73596351a8cc..60854d76c97a 100644 --- a/llvm/test/Transforms/SimplifyLibCalls/StrCmp.ll +++ b/llvm/test/Transforms/SimplifyLibCalls/StrCmp.ll @@ -1,28 +1,65 @@ ; Test that the StrCmpOptimizer works correctly -; RUN: opt < %s -simplify-libcalls -S | \ -; RUN: not grep {call.*strcmp} +; RUN: opt < %s -simplify-libcalls -S | FileCheck %s + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128" @hello = constant [6 x i8] c"hello\00" ; <[6 x i8]*> [#uses=1] @hell = constant [5 x i8] c"hell\00" ; <[5 x i8]*> [#uses=1] +@bell = constant [5 x i8] c"bell\00" ; <[5 x i8]*> [#uses=1] @null = constant [1 x i8] zeroinitializer ; <[1 x i8]*> [#uses=1] declare i32 @strcmp(i8*, i8*) -declare i32 @puts(i8*) - -define i32 @main() { - %hello_p = getelementptr [6 x i8]* @hello, i32 0, i32 0 ; [#uses=5] - %hell_p = getelementptr [5 x i8]* @hell, i32 0, i32 0 ; [#uses=1] - %null_p = getelementptr [1 x i8]* @null, i32 0, i32 0 ; [#uses=4] - %temp1 = call i32 @strcmp( i8* %hello_p, i8* %hello_p ) ; [#uses=1] - %temp2 = call i32 @strcmp( i8* %null_p, i8* %null_p ) ; [#uses=1] - %temp3 = call i32 @strcmp( i8* %hello_p, i8* %null_p ) ; [#uses=1] - %temp4 = call i32 @strcmp( i8* %null_p, i8* %hello_p ) ; [#uses=1] - %temp5 = call i32 @strcmp( i8* %hell_p, i8* %hello_p ) ; [#uses=1] - %rslt1 = add i32 %temp1, %temp2 ; [#uses=1] - %rslt2 = add i32 %rslt1, %temp3 ; [#uses=1] - %rslt3 = add i32 %rslt2, %temp4 ; [#uses=1] - %rslt4 = add i32 %rslt3, %temp5 ; [#uses=1] - ret i32 %rslt4 +; strcmp("", x) -> -*x +define i32 @test1(i8* %str) { + %temp1 = call i32 @strcmp(i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0), i8* %str) + ret i32 %temp1 + ; CHECK: @test1 + ; CHECK: %strcmpload = load i8* %str + ; CHECK: %1 = zext i8 %strcmpload to i32 + ; CHECK: %temp1 = sub i32 0, %1 + ; CHECK: ret i32 %temp1 } +; strcmp(x, "") -> *x +define i32 @test2(i8* %str) { + %temp1 = call i32 @strcmp(i8* %str, i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0)) + ret i32 %temp1 + ; CHECK: @test2 + ; CHECK: %strcmpload = load i8* %str + ; CHECK: %temp1 = zext i8 %strcmpload to i32 + ; CHECK: ret i32 %temp1 +} + +; strcmp(x, y) -> cnst +define i32 @test3() { + %temp1 = call i32 @strcmp(i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8]* @hello, i32 0, i32 0)) + ret i32 %temp1 + ; CHECK: @test3 + ; CHECK: ret i32 -1 +} +define i32 @test4() { + %temp1 = call i32 @strcmp(i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0)) + ret i32 %temp1 + ; CHECK: @test4 + ; CHECK: ret i32 1 +} + +; strcmp(x, y) -> memcmp(x, y, ) +; (This transform is rather difficult to trigger in a useful manner) +define i32 @test5(i1 %b) { + %sel = select i1 %b, i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8]* @bell, i32 0, i32 0) + %temp1 = call i32 @strcmp(i8* getelementptr inbounds ([6 x i8]* @hello, i32 0, i32 0), i8* %sel) + ret i32 %temp1 + ; CHECK: @test5 + ; CHECK: %memcmp = call i32 @memcmp(i8* getelementptr inbounds ([6 x i8]* @hello, i32 0, i32 0), i8* %sel, i32 5) + ; CHECK: ret i32 %memcmp +} + +; strcmp(x,x) -> 0 +define i32 @test6(i8* %str) { + %temp1 = call i32 @strcmp(i8* %str, i8* %str) + ret i32 %temp1 + ; CHECK: @test6 + ; CHECK: ret i32 0 +} diff --git a/llvm/test/Transforms/SimplifyLibCalls/StrNCmp.ll b/llvm/test/Transforms/SimplifyLibCalls/StrNCmp.ll index 8b536a548699..0b2a501a3c8a 100644 --- a/llvm/test/Transforms/SimplifyLibCalls/StrNCmp.ll +++ b/llvm/test/Transforms/SimplifyLibCalls/StrNCmp.ll @@ -1,35 +1,78 @@ -; Test that the StrNCmpOptimizer works correctly -; RUN: opt < %s -simplify-libcalls -S | \ -; RUN: not grep {call.*strncmp} +; Test that the StrCmpOptimizer works correctly +; RUN: opt < %s -simplify-libcalls -S | FileCheck %s -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32" -target triple = "i386-apple-darwin9.0" +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128" @hello = constant [6 x i8] c"hello\00" ; <[6 x i8]*> [#uses=1] @hell = constant [5 x i8] c"hell\00" ; <[5 x i8]*> [#uses=1] +@bell = constant [5 x i8] c"bell\00" ; <[5 x i8]*> [#uses=1] @null = constant [1 x i8] zeroinitializer ; <[1 x i8]*> [#uses=1] declare i32 @strncmp(i8*, i8*, i32) -declare i32 @puts(i8*) - -define i32 @main() { - %hello_p = getelementptr [6 x i8]* @hello, i32 0, i32 0 ; [#uses=5] - %hell_p = getelementptr [5 x i8]* @hell, i32 0, i32 0 ; [#uses=1] - %null_p = getelementptr [1 x i8]* @null, i32 0, i32 0 ; [#uses=4] - %temp1 = call i32 @strncmp( i8* %hello_p, i8* %hello_p, i32 5 ) ; [#uses=1] - %temp2 = call i32 @strncmp( i8* %null_p, i8* %null_p, i32 0 ) ; [#uses=1] - %temp3 = call i32 @strncmp( i8* %hello_p, i8* %null_p, i32 0 ) ; [#uses=1] - %temp4 = call i32 @strncmp( i8* %null_p, i8* %hello_p, i32 0 ) ; [#uses=1] - %temp5 = call i32 @strncmp( i8* %hell_p, i8* %hello_p, i32 4 ) ; [#uses=1] - %rslt1 = add i32 %temp1, %temp2 ; [#uses=1] - %rslt2 = add i32 %rslt1, %temp3 ; [#uses=1] - %rslt3 = add i32 %rslt2, %temp4 ; [#uses=1] - %rslt4 = add i32 %rslt3, %temp5 ; [#uses=1] - ret i32 %rslt4 +; strcmp("", x) -> -*x +define i32 @test1(i8* %str) { + %temp1 = call i32 @strncmp(i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0), i8* %str, i32 10) + ret i32 %temp1 + ; CHECK: @test1 + ; CHECK: %strcmpload = load i8* %str + ; CHECK: %1 = zext i8 %strcmpload to i32 + ; CHECK: %temp1 = sub i32 0, %1 + ; CHECK: ret i32 %temp1 } -define i32 @test1(i8* %P, i8* %Q) { - %cmp = call i32 @strncmp(i8* %P, i8* %Q, i32 1) - ret i32 %cmp +; strcmp(x, "") -> *x +define i32 @test2(i8* %str) { + %temp1 = call i32 @strncmp(i8* %str, i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0), i32 10) + ret i32 %temp1 + ; CHECK: @test2 + ; CHECK: %strcmpload = load i8* %str + ; CHECK: %temp1 = zext i8 %strcmpload to i32 + ; CHECK: ret i32 %temp1 +} + +; strncmp(x, y, n) -> cnst +define i32 @test3() { + %temp1 = call i32 @strncmp(i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8]* @hello, i32 0, i32 0), i32 10) + ret i32 %temp1 + ; CHECK: @test3 + ; CHECK: ret i32 -1 +} +define i32 @test4() { + %temp1 = call i32 @strncmp(i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([1 x i8]* @null, i32 0, i32 0), i32 10) + ret i32 %temp1 + ; CHECK: @test4 + ; CHECK: ret i32 1 +} +define i32 @test5() { + %temp1 = call i32 @strncmp(i8* getelementptr inbounds ([5 x i8]* @hell, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8]* @hello, i32 0, i32 0), i32 4) + ret i32 %temp1 + ; CHECK: @test5 + ; CHECK: ret i32 0 +} + +; strncmp(x,y,1) -> memcmp(x,y,1) +define i32 @test6(i8* %str1, i8* %str2) { + %temp1 = call i32 @strncmp(i8* %str1, i8* %str2, i32 1) + ret i32 %temp1 + ; CHECK: @test6 + ; CHECK: load i8* + ; CHECK: load i8* + ; CHECK: sub i32 +} + +; strncmp(x,y,0) -> 0 +define i32 @test7(i8* %str1, i8* %str2) { + %temp1 = call i32 @strncmp(i8* %str1, i8* %str2, i32 0) + ret i32 %temp1 + ; CHECK: @test7 + ; CHECK: ret i32 0 +} + +; strncmp(x,x,n) -> 0 +define i32 @test8(i8* %str, i32 %n) { + %temp1 = call i32 @strncmp(i8* %str, i8* %str, i32 %n) + ret i32 %temp1 + ; CHECK: @test8 + ; CHECK: ret i32 0 }