diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index a975f1c55d7af8..8fe98b5ba427ad 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5197,6 +5197,31 @@ bool llvm::isOverflowIntrinsicNoWrap(const WithOverflowInst *WO, return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch); } +/// Shifts return poison if shiftwidth is larger than the bitwidth. +static bool shiftAmountKnownInRange(const Value *ShiftAmount) { + auto *C = dyn_cast(ShiftAmount); + if (!C) + return false; + + // Shifts return poison if shiftwidth is larger than the bitwidth. + SmallVector ShiftAmounts; + if (auto *FVTy = dyn_cast(C->getType())) { + unsigned NumElts = FVTy->getNumElements(); + for (unsigned i = 0; i < NumElts; ++i) + ShiftAmounts.push_back(C->getAggregateElement(i)); + } else if (isa(C->getType())) + return false; // Can't tell, just return false to be safe + else + ShiftAmounts.push_back(C); + + bool Safe = llvm::all_of(ShiftAmounts, [](const Constant *C) { + auto *CI = dyn_cast_or_null(C); + return CI && CI->getValue().ult(C->getType()->getIntegerBitWidth()); + }); + + return Safe; +} + static bool canCreateUndefOrPoison(const Operator *Op, bool PoisonOnly, bool ConsiderFlags) { @@ -5209,27 +5234,8 @@ static bool canCreateUndefOrPoison(const Operator *Op, bool PoisonOnly, switch (Opcode) { case Instruction::Shl: case Instruction::AShr: - case Instruction::LShr: { - // Shifts return poison if shiftwidth is larger than the bitwidth. - if (auto *C = dyn_cast(Op->getOperand(1))) { - SmallVector ShiftAmounts; - if (auto *FVTy = dyn_cast(C->getType())) { - unsigned NumElts = FVTy->getNumElements(); - for (unsigned i = 0; i < NumElts; ++i) - ShiftAmounts.push_back(C->getAggregateElement(i)); - } else if (isa(C->getType())) - return true; // Can't tell, just return true to be safe - else - ShiftAmounts.push_back(C); - - bool Safe = llvm::all_of(ShiftAmounts, [](Constant *C) { - auto *CI = dyn_cast_or_null(C); - return CI && CI->getValue().ult(C->getType()->getIntegerBitWidth()); - }); - return !Safe; - } - return true; - } + case Instruction::LShr: + return !shiftAmountKnownInRange(Op->getOperand(1)); case Instruction::FPToSI: case Instruction::FPToUI: // fptosi/ui yields poison if the resulting value does not fit in the @@ -5267,8 +5273,10 @@ static bool canCreateUndefOrPoison(const Operator *Op, bool PoisonOnly, case Intrinsic::uadd_sat: case Intrinsic::ssub_sat: case Intrinsic::usub_sat: + return false; case Intrinsic::sshl_sat: case Intrinsic::ushl_sat: + return !shiftAmountKnownInRange(II->getArgOperand(1)); case Intrinsic::fma: case Intrinsic::fmuladd: case Intrinsic::sqrt: diff --git a/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll b/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll index 122f4f260eb01e..fa62cbcd24dc18 100644 --- a/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll @@ -286,9 +286,9 @@ define i32 @usub_sat_i32(i32 %arg0, i32 noundef %arg1) { define i32 @sshl_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @sshl_sat_i32( -; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) -; CHECK-NEXT: ret i32 [[CALL]] +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] +; CHECK-NEXT: ret i32 [[FREEZE]] ; %call = call i32 @llvm.sshl.sat.i32(i32 %arg0, i32 %arg1) %freeze = freeze i32 %call @@ -297,15 +297,125 @@ define i32 @sshl_sat_i32(i32 %arg0, i32 noundef %arg1) { define i32 @ushl_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @ushl_sat_i32( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] +; CHECK-NEXT: ret i32 [[FREEZE]] +; + %call = call i32 @llvm.ushl.sat.i32(i32 %arg0, i32 %arg1) + %freeze = freeze i32 %call + ret i32 %freeze +} + +define i32 @sshl_sat_i32_safe_constant(i32 %arg0) { +; CHECK-LABEL: @sshl_sat_i32_safe_constant( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0_FR]], i32 8) ; CHECK-NEXT: ret i32 [[CALL]] ; - %call = call i32 @llvm.ushl.sat.i32(i32 %arg0, i32 %arg1) + %call = call i32 @llvm.sshl.sat.i32(i32 %arg0, i32 8) + %freeze = freeze i32 %call + ret i32 %freeze +} + +define i32 @ushl_sat_i32_safe_constant(i32 %arg0) { +; CHECK-LABEL: @ushl_sat_i32_safe_constant( +; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0_FR]], i32 8) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 @llvm.ushl.sat.i32(i32 %arg0, i32 8) + %freeze = freeze i32 %call + ret i32 %freeze +} + +define i32 @sshl_sat_i32_unsafe_constant(i32 %arg0) { +; CHECK-LABEL: @sshl_sat_i32_unsafe_constant( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0:%.*]], i32 32) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] +; CHECK-NEXT: ret i32 [[FREEZE]] +; + %call = call i32 @llvm.sshl.sat.i32(i32 %arg0, i32 32) %freeze = freeze i32 %call ret i32 %freeze } +define i32 @ushl_sat_i32_unsafe_constant(i32 %arg0) { +; CHECK-LABEL: @ushl_sat_i32_unsafe_constant( +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0:%.*]], i32 32) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] +; CHECK-NEXT: ret i32 [[FREEZE]] +; + %call = call i32 @llvm.ushl.sat.i32(i32 %arg0, i32 32) + %freeze = freeze i32 %call + ret i32 %freeze +} + +define <2 x i32> @sshl_sat_v2i32_safe_constant(<2 x i32> %arg0) { +; CHECK-LABEL: @sshl_sat_v2i32_safe_constant( +; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze <2 x i32> [[ARG0:%.*]] +; CHECK-NEXT: [[CALL:%.*]] = call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> [[ARG0_FR]], <2 x i32> ) +; CHECK-NEXT: ret <2 x i32> [[CALL]] +; + %call = call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> %arg0, <2 x i32> ) + %freeze = freeze <2 x i32> %call + ret <2 x i32> %freeze +} + +define <2 x i32> @ushl_sat_v2i32_safe_constant_vector(<2 x i32> %arg0) { +; CHECK-LABEL: @ushl_sat_v2i32_safe_constant_vector( +; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze <2 x i32> [[ARG0:%.*]] +; CHECK-NEXT: [[CALL:%.*]] = call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> [[ARG0_FR]], <2 x i32> ) +; CHECK-NEXT: ret <2 x i32> [[CALL]] +; + %call = call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> %arg0, <2 x i32> ) + %freeze = freeze <2 x i32> %call + ret <2 x i32> %freeze +} + +define <2 x i32> @ushl_sat_v2i32_unsafe_constant_vector(<2 x i32> %arg0) { +; CHECK-LABEL: @ushl_sat_v2i32_unsafe_constant_vector( +; CHECK-NEXT: [[CALL:%.*]] = call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> [[ARG0:%.*]], <2 x i32> ) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze <2 x i32> [[CALL]] +; CHECK-NEXT: ret <2 x i32> [[FREEZE]] +; + %call = call <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32> %arg0, <2 x i32> ) + %freeze = freeze <2 x i32> %call + ret <2 x i32> %freeze +} + +define <2 x i32> @sshl_sat_v2i32_unsafe_constant_vector(<2 x i32> %arg0) { +; CHECK-LABEL: @sshl_sat_v2i32_unsafe_constant_vector( +; CHECK-NEXT: [[CALL:%.*]] = call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> [[ARG0:%.*]], <2 x i32> ) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze <2 x i32> [[CALL]] +; CHECK-NEXT: ret <2 x i32> [[FREEZE]] +; + %call = call <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32> %arg0, <2 x i32> ) + %freeze = freeze <2 x i32> %call + ret <2 x i32> %freeze +} + +define @ushl_sat_v2i32_scalable_zeroinitializer( %arg0) { +; CHECK-LABEL: @ushl_sat_v2i32_scalable_zeroinitializer( +; CHECK-NEXT: [[CALL:%.*]] = call @llvm.ushl.sat.nxv2i32( [[ARG0:%.*]], zeroinitializer) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze [[CALL]] +; CHECK-NEXT: ret [[FREEZE]] +; + %call = call @llvm.ushl.sat.nxv2i32( %arg0, zeroinitializer) + %freeze = freeze %call + ret %freeze +} + +define @sshl_sat_v2i32_scalable_zeroinitializer( %arg0) { +; CHECK-LABEL: @sshl_sat_v2i32_scalable_zeroinitializer( +; CHECK-NEXT: [[CALL:%.*]] = call @llvm.sshl.sat.nxv2i32( [[ARG0:%.*]], zeroinitializer) +; CHECK-NEXT: [[FREEZE:%.*]] = freeze [[CALL]] +; CHECK-NEXT: ret [[FREEZE]] +; + %call = call @llvm.sshl.sat.nxv2i32( %arg0, zeroinitializer) + %freeze = freeze %call + ret %freeze +} + declare i32 @llvm.ctlz.i32(i32, i1 immarg) declare i32 @llvm.cttz.i32(i32, i1 immarg) declare i32 @llvm.abs.i32(i32, i1 immarg) @@ -326,3 +436,7 @@ declare i32 @llvm.ssub.sat.i32(i32, i32) declare i32 @llvm.usub.sat.i32(i32, i32) declare i32 @llvm.sshl.sat.i32(i32, i32) declare i32 @llvm.ushl.sat.i32(i32, i32) +declare <2 x i32> @llvm.sshl.sat.v2i32(<2 x i32>, <2 x i32>) +declare <2 x i32> @llvm.ushl.sat.v2i32(<2 x i32>, <2 x i32>) +declare @llvm.sshl.sat.nxv2i32(, ) +declare @llvm.ushl.sat.nxv2i32(, )