Index: llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -563,6 +563,34 @@ return B.CreateZExt(B.CreateLoad(B.getInt8Ty(), Str1P, "strcmpload"), CI->getType()); + // Fold strcmp(P, "x"), strcmp(P, "xx") + if (HasStr2 && (Str2.size() == 1 || Str2.size() == 2)) { + auto *Str1FirstCharacterValue = B.CreateIntCast( + B.CreateLoad(B.getInt8Ty(), Str1P), B.getInt32Ty(), true); + auto *Str2FirstCharacterValue = ConstantInt::get( + B.getInt32Ty(), static_cast(Str2[0]), true); + auto *FirstCharacterSub = + B.CreateNSWSub(Str1FirstCharacterValue, Str2FirstCharacterValue); + + if (Str2.size() == 1) { + return FirstCharacterSub; + } + + auto *Str1SecondCharacterValue = B.CreateIntCast( + B.CreateLoad(B.getInt8Ty(), + B.CreateConstInBoundsGEP1_32(B.getInt8Ty(), Str1P, 1)), + B.getInt32Ty(), true); + auto *Str2SecondCharacterValue = ConstantInt::get( + B.getInt32Ty(), static_cast(Str2[1]), true); + auto *SecondCharacterSub = + B.CreateNSWSub(Str1SecondCharacterValue, Str2SecondCharacterValue); + + return B.CreateSelect( + B.CreateICmpEQ(FirstCharacterSub, + ConstantInt::get(B.getInt32Ty(), 0, true)), + SecondCharacterSub, FirstCharacterSub); + } + // strcmp(P, "x") -> memcmp(P, "x", 2) uint64_t Len1 = GetStringLength(Str1P); if (Len1) Index: llvm/test/Transforms/InstCombine/strcmp-5.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/strcmp-5.ll @@ -0,0 +1,64 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; TODO: Test that ... +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare i32 @strcmp(ptr, ptr) + +@s1 = constant [2 x i8] c"0\00" +@s2 = constant [3 x i8] c"05\00" + +; Fold strcmp(C, "x"), strcmp(C, "xx"). + +define i1 @fold_strcmp_s1_1(ptr %C) { +; CHECK-LABEL: @fold_strcmp_s1_1( +; CHECK-NEXT: [[TMP:%.*]] = load i8, ptr %C, align 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[TMP]], 48 +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = call i32 @strcmp(ptr %C, ptr noundef @s1) + %cmp = icmp eq i32 %call, 0 + ret i1 %cmp +} + +define i32 @fold_strcmp_s1_2(ptr %C) { +; CHECK-LABEL: @fold_strcmp_s1_2( +; CHECK-NEXT: [[TMP:%.*]] = load i8, ptr %C, align 1 +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[TMP]] to i32 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SEXT]], -48 +; CHECK-NEXT: ret i32 [[ADD]] +; + %call = call i32 @strcmp(ptr %C, ptr noundef @s1) + ret i32 %call +} + +define i1 @fold_strcmp_s2_1(ptr %C) { +; CHECK-LABEL: @fold_strcmp_s2_1( +; CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr %C, align 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr %C, i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[TMP1]], 48 +; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[TMP3]], 53 +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false +; CHECK-NEXT: ret i1 [[SELECT]] +; + %call = call i32 @strcmp(ptr %C, ptr noundef @s2) + %cmp = icmp eq i32 %call, 0 + ret i1 %cmp +} + +define i32 @fold_strcmp_s2_2(ptr %C) { +; CHECK-LABEL: @fold_strcmp_s2_2( +; CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr %C, align 1 +; CHECK-NEXT: [[SEXT1:%.*]] = sext i8 [[TMP1]] to i32 +; CHECK-NEXT: [[ADD1:%.*]] = add nsw i32 [[SEXT1]], -48 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr %C, i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1 +; CHECK-NEXT: [[SEXT2:%.*]] = sext i8 [[TMP3]] to i32 +; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[SEXT2]], -53 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ADD1]], 0 +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[CMP]], i32 [[ADD2]], i32 [[ADD1]] +; CHECK-NEXT: ret i32 [[SELECT]] +; + %call = call i32 @strcmp(ptr %C, ptr noundef @s2) + ret i32 %call +}