Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -469,6 +469,12 @@ KnownBits Known = IC.computeKnownBits(Op0, 0, &II); + if (!IsTZ && Known.isNegative()) { + // ctlz(x) -> 0 if x is known negative. + auto *C = Constant::getNullValue(Op0->getType()); + return IC.replaceInstUsesWith(II, C); + } + // Create a mask for bits above (ctlz) or below (cttz) the first known one. unsigned PossibleZeros = IsTZ ? Known.countMaxTrailingZeros() : Known.countMaxLeadingZeros(); Index: llvm/test/Transforms/InstCombine/ctlz.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/ctlz.ll @@ -0,0 +1,71 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -instcombine -S < %s | FileCheck %s +declare i32 @llvm.ctlz.i32(i32, i1) +declare void @llvm.assume(i1) +declare void @use(i32) + +define i32 @ctlz_sext_negative_zero_undef(i16 %x) { +; CHECK-LABEL: @ctlz_sext_negative_zero_undef( +; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) +; CHECK-NEXT: ret i32 0 +; + %assume = icmp slt i16 %x, 0 + call void @llvm.assume(i1 %assume) + %s = sext i16 %x to i32 + %c = call i32 @llvm.ctlz.i32(i32 %s, i1 true) + ret i32 %c +} + +define i32 @ctlz_negative_zero_undef(i32 %x) { +; CHECK-LABEL: @ctlz_negative_zero_undef( +; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i32 [[X:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) +; CHECK-NEXT: ret i32 0 +; + %assume = icmp slt i32 %x, 0 + call void @llvm.assume(i1 %assume) + %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true) + ret i32 %c +} + +define i32 @ctlz_negative_zero_def(i32 %x) { +; CHECK-LABEL: @ctlz_negative_zero_def( +; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i32 [[X:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) +; CHECK-NEXT: ret i32 0 +; + %assume = icmp slt i32 %x, 0 + call void @llvm.assume(i1 %assume) + %c = call i32 @llvm.ctlz.i32(i32 %x, i1 false) + ret i32 %c +} + +define i32 @ctlz_sext_negative_zero_def(i16 %x) { +; CHECK-LABEL: @ctlz_sext_negative_zero_def( +; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) +; CHECK-NEXT: ret i32 0 +; + %assume = icmp slt i16 %x, 0 + call void @llvm.assume(i1 %assume) + %s = sext i16 %x to i32 + %c = call i32 @llvm.ctlz.i32(i32 %s, i1 false) + ret i32 %c +} + +define i32 @ctlz_sext_negative_zero_undef_extra_use(i16 %x) { +; CHECK-LABEL: @ctlz_sext_negative_zero_undef_extra_use( +; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 +; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) +; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32 +; CHECK-NEXT: call void @use(i32 [[S]]) +; CHECK-NEXT: ret i32 0 +; + %assume = icmp slt i16 %x, 0 + call void @llvm.assume(i1 %assume) + %s = sext i16 %x to i32 + call void @use(i32 %s) + %c = call i32 @llvm.ctlz.i32(i32 %s, i1 true) + ret i32 %c +} Index: llvm/test/Transforms/InstSimplify/call.ll =================================================================== --- llvm/test/Transforms/InstSimplify/call.ll +++ llvm/test/Transforms/InstSimplify/call.ll @@ -1465,78 +1465,4 @@ ret <3 x i33> %r } -define i32 @ctlz_sext_negative_zero_undef(i16 %x) { -; CHECK-LABEL: @ctlz_sext_negative_zero_undef( -; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 -; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) -; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32 -; CHECK-NEXT: [[C:%.*]] = call i32 @llvm.ctlz.i32(i32 [[S]], i1 true) -; CHECK-NEXT: ret i32 [[C]] -; - %assume = icmp slt i16 %x, 0 - call void @llvm.assume(i1 %assume) - %s = sext i16 %x to i32 - %c = call i32 @llvm.ctlz.i32(i32 %s, i1 true) - ret i32 %c -} - -define i32 @ctlz_negative_zero_undef(i32 %x) { -; CHECK-LABEL: @ctlz_negative_zero_undef( -; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i32 [[X:%.*]], 0 -; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) -; CHECK-NEXT: [[C:%.*]] = call i32 @llvm.ctlz.i32(i32 [[X]], i1 true) -; CHECK-NEXT: ret i32 [[C]] -; - %assume = icmp slt i32 %x, 0 - call void @llvm.assume(i1 %assume) - %c = call i32 @llvm.ctlz.i32(i32 %x, i1 true) - ret i32 %c -} - -define i32 @ctlz_negative_zero_def(i32 %x) { -; CHECK-LABEL: @ctlz_negative_zero_def( -; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i32 [[X:%.*]], 0 -; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) -; CHECK-NEXT: [[C:%.*]] = call i32 @llvm.ctlz.i32(i32 [[X]], i1 false) -; CHECK-NEXT: ret i32 [[C]] -; - %assume = icmp slt i32 %x, 0 - call void @llvm.assume(i1 %assume) - %c = call i32 @llvm.ctlz.i32(i32 %x, i1 false) - ret i32 %c -} - -define i32 @ctlz_sext_negative_zero_def(i16 %x) { -; CHECK-LABEL: @ctlz_sext_negative_zero_def( -; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 -; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) -; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32 -; CHECK-NEXT: [[C:%.*]] = call i32 @llvm.ctlz.i32(i32 [[S]], i1 false) -; CHECK-NEXT: ret i32 [[C]] -; - %assume = icmp slt i16 %x, 0 - call void @llvm.assume(i1 %assume) - %s = sext i16 %x to i32 - %c = call i32 @llvm.ctlz.i32(i32 %s, i1 false) - ret i32 %c -} - -declare void @use(i32) -define i32 @ctlz_sext_negative_zero_undef_extra_use(i16 %x) { -; CHECK-LABEL: @ctlz_sext_negative_zero_undef_extra_use( -; CHECK-NEXT: [[ASSUME:%.*]] = icmp slt i16 [[X:%.*]], 0 -; CHECK-NEXT: call void @llvm.assume(i1 [[ASSUME]]) -; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32 -; CHECK-NEXT: call void @use(i32 [[S]]) -; CHECK-NEXT: [[C:%.*]] = call i32 @llvm.ctlz.i32(i32 [[S]], i1 true) -; CHECK-NEXT: ret i32 [[C]] -; - %assume = icmp slt i16 %x, 0 - call void @llvm.assume(i1 %assume) - %s = sext i16 %x to i32 - call void @use(i32 %s) - %c = call i32 @llvm.ctlz.i32(i32 %s, i1 true) - ret i32 %c -} - attributes #0 = { nobuiltin readnone }