Index: lib/Transforms/InstCombine/InstCombineCompares.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCompares.cpp +++ lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1085,6 +1085,7 @@ return getICmp(I.ICMP_EQ, A, ConstantInt::getNullValue(A->getType())); } + bool AreBothNegative = false; if (IsAShr) { if (AP1.isNegative() != AP2.isNegative()) { // Arithmetic shift will never change the sign. @@ -1095,6 +1096,7 @@ if (AP1.isNegative()) { AP1 = -AP1; AP2 = -AP2; + AreBothNegative = true; } } @@ -1106,8 +1108,29 @@ // Get the distance between the highest bit that's set. int Shift = AP2.logBase2() - AP1.logBase2(); - // Use lshr here, since we've canonicalized to +ve numbers. - if (AP1 == AP2.lshr(Shift)) + // Revert the canonicalization before testing if shifting AP2 + // by 'Shift' count we can get AP1. It is wrong to assume that: + // + // (-AP2 >> Shift == -AP1) ==> (AP2 >> Shift == AP1) + // + // Example, with: + // AP2 = -9; + // AP1 = -5; + // + // With 'Shift' equal to 1, we have that: + // -9 >> 1 == -5 + // but + // 9 >> 1 != 5. + bool CanFoldCompare = false; + if (AreBothNegative) { + // Undo the canonicalization. + AP1 = -AP1; + AP2 = -AP2; + CanFoldCompare = AP1 == AP2.ashr(Shift); + } else + CanFoldCompare = AP1 == AP2.lshr(Shift); + + if (CanFoldCompare) return getICmp(I.ICMP_EQ, A, ConstantInt::get(A->getType(), Shift)); // Shifting const2 will never be equal to const1. Index: test/Transforms/InstCombine/icmp-shr.ll =================================================================== --- test/Transforms/InstCombine/icmp-shr.ll +++ test/Transforms/InstCombine/icmp-shr.ll @@ -675,3 +675,30 @@ %cmp = icmp ne i8 %shr, -30 ret i1 %cmp } + +; Don't try to fold the entire body of function @negative_constants into a +; single `ret i32 0` statement. +; If %B is equal to 1, then this function would return 42. That is because +; expression `-9 >> B` evaluates to -5 only if %B is equal to 1. +; As a consequence, the instruction combiner should never fold %cmp to true. +; Instead, it should simplify the comparison between %shr and -5 emitting a +; simpler comparison between %B and 1. +; Later on, this would allow to further simplify the CFG and emit a single +; 'select' instruction instead of a compare+branch. + +; CHECK-LABEL: @negative_constants( +; CHECK: icmp eq i32 %B, 1 +define i32 @negative_constants(i32 %B) { +entry: + %shr = ashr i32 -9, %B + %cmp = icmp ne i32 %shr, -5 + br i1 %cmp, label %if.then, label %return + +if.then: + br label %return + +return: + %retval = phi i32 [ 0, %if.then ], [ 42, %entry ] + ret i32 %retval +} +