Index: llvm/lib/Analysis/ConstantFolding.cpp =================================================================== --- llvm/lib/Analysis/ConstantFolding.cpp +++ llvm/lib/Analysis/ConstantFolding.cpp @@ -1589,6 +1589,7 @@ case Intrinsic::cos: case Intrinsic::pow: case Intrinsic::powi: + case Intrinsic::ldexp: case Intrinsic::fma: case Intrinsic::fmuladd: case Intrinsic::fptoui_sat: @@ -1603,7 +1604,6 @@ case Intrinsic::amdgcn_fmul_legacy: case Intrinsic::amdgcn_fma_legacy: case Intrinsic::amdgcn_fract: - case Intrinsic::amdgcn_ldexp: case Intrinsic::amdgcn_sin: // The intrinsics below depend on rounding mode in MXCSR. case Intrinsic::x86_sse_cvtss2si: @@ -2665,6 +2665,11 @@ } } else if (auto *Op2C = dyn_cast(Operands[1])) { switch (IntrinsicID) { + case Intrinsic::ldexp: { + return ConstantFP::get( + Ty->getContext(), + scalbn(Op1V, Op2C->getSExtValue(), APFloat::rmNearestTiesToEven)); + } case Intrinsic::is_fpclass: { FPClassTest Mask = static_cast(Op2C->getZExtValue()); bool Result = @@ -2702,9 +2707,10 @@ APFloat((double)std::pow(Op1V.convertToDouble(), (int)Op2C->getZExtValue()))); - if (IntrinsicID == Intrinsic::amdgcn_ldexp) { + if (IntrinsicID == Intrinsic::ldexp) { // FIXME: Should flush denorms depending on FP mode, but that's ignored // everywhere else. + // TODO: Can we fold constrained ldexp and ignore denorm mode? // scalbn is equivalent to ldexp with float radix 2 APFloat Result = scalbn(Op1->getValueAPF(), Op2C->getSExtValue(), Index: llvm/lib/Analysis/InstructionSimplify.cpp =================================================================== --- llvm/lib/Analysis/InstructionSimplify.cpp +++ llvm/lib/Analysis/InstructionSimplify.cpp @@ -6071,6 +6071,47 @@ return ConstantExpr::getBitCast(LoadedLHSPtr, Int8PtrTy); } +static Value *simplifyLdexp(Value *Op0, Value *Op1, const SimplifyQuery &Q, + bool IsStrict) { + // ldexp(poison, x) -> poison + // ldexp(x, poison) -> poison + if (isa(Op0) || isa(Op1)) + return Op0; + + if (!IsStrict) { + // ldexp(undef, x) -> nan + if (Q.isUndefValue(Op0)) + return ConstantFP::getNaN(Op0->getType()); + + // ldexp(x, undef) -> x + if (Q.isUndefValue(Op1)) + return Op0; + } + + const APFloat *C = nullptr; + match(Op0, PatternMatch::m_APFloat(C)); + + // These cases should be safe, even with strictfp. + // ldexp(0.0, x) -> 0.0 + // ldexp(-0.0, x) -> -0.0 + // ldexp(inf, x) -> inf + // ldexp(-inf, x) -> -inf + if (C && (C->isZero() || C->isInfinity())) + return Op0; + + if (IsStrict) + return nullptr; + + if (C && C->isNaN()) + return ConstantFP::get(Op0->getType(), C->makeQuiet()); + + // ldexp(x, 0) -> x + if (match(Op1, PatternMatch::m_ZeroInt())) + return Op0; + + return nullptr; +} + static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, const SimplifyQuery &Q) { // Idempotent functions return the same result when called repeatedly. @@ -6405,6 +6446,8 @@ return Op0; } break; + case Intrinsic::ldexp: + return simplifyLdexp(Op0, Op1, Q, false); case Intrinsic::copysign: // copysign X, X --> X if (Op0 == Op1) @@ -6671,6 +6714,8 @@ *FPI->getExceptionBehavior(), *FPI->getRoundingMode()); } + case Intrinsic::experimental_constrained_ldexp: + return simplifyLdexp(Args[0], Args[1], Q, true); default: return nullptr; } Index: llvm/test/Transforms/InstCombine/AMDGPU/ldexp.ll =================================================================== --- llvm/test/Transforms/InstCombine/AMDGPU/ldexp.ll +++ /dev/null @@ -1,342 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt < %s -mtriple=amdgcn-amd-amdhsa -passes=instcombine -S | FileCheck %s - -define float @ldexp_f32_undef_undef() { -; CHECK-LABEL: @ldexp_f32_undef_undef( -; CHECK-NEXT: ret float 0x7FF8000000000000 -; - %call = call float @llvm.amdgcn.ldexp.f32(float undef, i32 undef) - ret float %call -} - -; If the exponent is 0, it doesn't matter if the first argument is -; constant or not. -define void @ldexp_f32_exp0(float %x) { -; CHECK-LABEL: @ldexp_f32_exp0( -; CHECK-NEXT: store volatile float [[X:%.*]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float [[X]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[ONE:%.*]] = call float @llvm.amdgcn.ldexp.f32(float [[X]], i32 1) -; CHECK-NEXT: store volatile float [[ONE]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %zero = call float @llvm.amdgcn.ldexp.f32(float %x, i32 0) - store volatile float %zero, ptr addrspace(1) undef - - %undef = call float @llvm.amdgcn.ldexp.f32(float %x, i32 undef) - store volatile float %undef, ptr addrspace(1) undef - - %one = call float @llvm.amdgcn.ldexp.f32(float %x, i32 1) - store volatile float %one, ptr addrspace(1) undef - ret void -} - -; Test variable exponent but zero or undef value. -define void @ldexp_f32_val0(i32 %y) { -; CHECK-LABEL: @ldexp_f32_val0( -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x7FF8000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %zero = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 %y) - store volatile float %zero, ptr addrspace(1) undef - - %neg.zero = call float @llvm.amdgcn.ldexp.f32(float -0.0, i32 %y) - store volatile float %neg.zero, ptr addrspace(1) undef - - %undef = call float @llvm.amdgcn.ldexp.f32(float undef, i32 %y) - store volatile float %undef, ptr addrspace(1) undef - ret void -} - -define void @ldexp_f32_val_infinity(i32 %y) { -; CHECK-LABEL: @ldexp_f32_val_infinity( -; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %inf = call float @llvm.amdgcn.ldexp.f32(float 0x7ff0000000000000, i32 %y) - store volatile float %inf, ptr addrspace(1) undef - - %neg.inf = call float @llvm.amdgcn.ldexp.f32(float 0xfff0000000000000, i32 %y) - store volatile float %neg.inf, ptr addrspace(1) undef - - %inf.zero = call float @llvm.amdgcn.ldexp.f32(float 0x7ff0000000000000, i32 0) - store volatile float %inf.zero, ptr addrspace(1) undef - - %neg.inf.zero = call float @llvm.amdgcn.ldexp.f32(float 0xfff0000000000000, i32 0) - store volatile float %neg.inf.zero, ptr addrspace(1) undef - - ret void -} - -; Signaling nan should be quieted. -; Technically this depends on the ieee_mode in the mode register. -define void @ldexp_f32_val_nan(i32 %y) { -; CHECK-LABEL: @ldexp_f32_val_nan( -; CHECK-NEXT: store volatile float 0x7FF8001000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xFFF8000100000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x7FF8000020000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xFFFFFFFFE0000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %plus.qnan = call float @llvm.amdgcn.ldexp.f32(float 0x7ff0001000000000, i32 %y) - store volatile float %plus.qnan, ptr addrspace(1) undef - - %neg.qnan = call float @llvm.amdgcn.ldexp.f32(float 0xfff0000100000000, i32 %y) - store volatile float %neg.qnan, ptr addrspace(1) undef - - %plus.snan = call float @llvm.amdgcn.ldexp.f32(float 0x7FF0000020000000, i32 %y) - store volatile float %plus.snan, ptr addrspace(1) undef - - %neg.snan = call float @llvm.amdgcn.ldexp.f32(float 0xFFF7FFFFE0000000, i32 %y) - store volatile float %neg.snan, ptr addrspace(1) undef - - ret void -} - -define void @ldexp_f32_val_nan_strictfp(i32 %y) #0 { -; CHECK-LABEL: @ldexp_f32_val_nan_strictfp( -; CHECK-NEXT: [[PLUS_QNAN:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0x7FF0001000000000, i32 [[Y:%.*]]) [[ATTR0:#.*]] -; CHECK-NEXT: store volatile float [[PLUS_QNAN]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[NEG_QNAN:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0xFFF0000100000000, i32 [[Y]]) [[ATTR0]] -; CHECK-NEXT: store volatile float [[NEG_QNAN]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[PLUS_SNAN:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0x7FF0000020000000, i32 [[Y]]) [[ATTR0]] -; CHECK-NEXT: store volatile float [[PLUS_SNAN]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[NEG_SNAN:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0xFFF7FFFFE0000000, i32 [[Y]]) [[ATTR0]] -; CHECK-NEXT: store volatile float [[NEG_SNAN]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x7FF8000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %plus.qnan = call float @llvm.amdgcn.ldexp.f32(float 0x7ff0001000000000, i32 %y) #0 - store volatile float %plus.qnan, ptr addrspace(1) undef - - %neg.qnan = call float @llvm.amdgcn.ldexp.f32(float 0xfff0000100000000, i32 %y) #0 - store volatile float %neg.qnan, ptr addrspace(1) undef - - %plus.snan = call float @llvm.amdgcn.ldexp.f32(float 0x7FF0000020000000, i32 %y) #0 - store volatile float %plus.snan, ptr addrspace(1) undef - - %neg.snan = call float @llvm.amdgcn.ldexp.f32(float 0xFFF7FFFFE0000000, i32 %y) #0 - store volatile float %neg.snan, ptr addrspace(1) undef - - %undef = call float @llvm.amdgcn.ldexp.f32(float undef, i32 %y) #0 - store volatile float %undef, ptr addrspace(1) undef - - ret void -} - -define void @ldexp_f32_0() { -; CHECK-LABEL: @ldexp_f32_0( -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %zero = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 0) - store volatile float %zero, ptr addrspace(1) undef - - %neg.zero = call float @llvm.amdgcn.ldexp.f32(float -0.0, i32 0) - store volatile float %neg.zero, ptr addrspace(1) undef - - %one = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 1) - store volatile float %one, ptr addrspace(1) undef - - %min.exp = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 -126) - store volatile float %min.exp, ptr addrspace(1) undef - - %min.exp.sub1 = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 -127) - store volatile float %min.exp.sub1, ptr addrspace(1) undef - - %max.exp = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 127) - store volatile float %max.exp, ptr addrspace(1) undef - - %max.exp.plus1 = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 128) - store volatile float %max.exp.plus1, ptr addrspace(1) undef - - ret void -} - -; Should be able to ignore strictfp in this case -define void @ldexp_f32_0_strictfp(float %x) #0 { -; CHECK-LABEL: @ldexp_f32_0_strictfp( -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[UNKNOWN_ZERO:%.*]] = call float @llvm.amdgcn.ldexp.f32(float [[X:%.*]], i32 0) [[ATTR0]] -; CHECK-NEXT: store volatile float [[UNKNOWN_ZERO]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[UNKNOWN_UNDEF:%.*]] = call float @llvm.amdgcn.ldexp.f32(float [[X]], i32 undef) [[ATTR0]] -; CHECK-NEXT: store volatile float [[UNKNOWN_UNDEF]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[DENORMAL_0:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 0) [[ATTR0]] -; CHECK-NEXT: store volatile float [[DENORMAL_0]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: [[DENORMAL_1:%.*]] = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 1) [[ATTR0]] -; CHECK-NEXT: store volatile float [[DENORMAL_1]], ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %zero = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 0) #0 - store volatile float %zero, ptr addrspace(1) undef - - %neg.zero = call float @llvm.amdgcn.ldexp.f32(float -0.0, i32 0) #0 - store volatile float %neg.zero, ptr addrspace(1) undef - - %one = call float @llvm.amdgcn.ldexp.f32(float 0.0, i32 1) #0 - store volatile float %one, ptr addrspace(1) undef - - %unknown.zero = call float @llvm.amdgcn.ldexp.f32(float %x, i32 0) #0 - store volatile float %unknown.zero, ptr addrspace(1) undef - - %unknown.undef = call float @llvm.amdgcn.ldexp.f32(float %x, i32 undef) #0 - store volatile float %unknown.undef, ptr addrspace(1) undef - - %denormal.0 = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 0) #0 - store volatile float %denormal.0, ptr addrspace(1) undef - - %denormal.1 = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 1) #0 - store volatile float %denormal.1, ptr addrspace(1) undef - - ret void -} - -define void @ldexp_f32() { -; CHECK-LABEL: @ldexp_f32( -; CHECK-NEXT: store volatile float 2.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 4.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 8.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 5.000000e-01, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x3810000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x3800000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x47E0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -2.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -4.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -8.000000e+00, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float -5.000000e-01, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xB810000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xB800000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xC7E0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x44D5000000000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %one.one = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 1) - store volatile float %one.one, ptr addrspace(1) undef - - %one.two = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 2) - store volatile float %one.two, ptr addrspace(1) undef - - %one.three = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 3) - store volatile float %one.three, ptr addrspace(1) undef - - %one.negone = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 -1) - store volatile float %one.negone, ptr addrspace(1) undef - - %one.min.exp = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 -126) - store volatile float %one.min.exp, ptr addrspace(1) undef - - %one.min.exp.sub1 = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 -127) - store volatile float %one.min.exp.sub1, ptr addrspace(1) undef - - %one.max.exp = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 127) - store volatile float %one.max.exp, ptr addrspace(1) undef - - %one.max.exp.plus1 = call float @llvm.amdgcn.ldexp.f32(float 1.0, i32 128) - store volatile float %one.max.exp.plus1, ptr addrspace(1) undef - - %neg.one.one = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 1) - store volatile float %neg.one.one, ptr addrspace(1) undef - - %neg.one.two = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 2) - store volatile float %neg.one.two, ptr addrspace(1) undef - - %neg.one.three = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 3) - store volatile float %neg.one.three, ptr addrspace(1) undef - - %neg.one.negone = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 -1) - store volatile float %neg.one.negone, ptr addrspace(1) undef - - %neg.one.min.exp = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 -126) - store volatile float %neg.one.min.exp, ptr addrspace(1) undef - - %neg.one.min.exp.sub1 = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 -127) - store volatile float %neg.one.min.exp.sub1, ptr addrspace(1) undef - - %neg.one.max.exp = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 127) - store volatile float %neg.one.max.exp, ptr addrspace(1) undef - - %neg.one.max.exp.plus1 = call float @llvm.amdgcn.ldexp.f32(float -1.0, i32 128) - store volatile float %neg.one.max.exp.plus1, ptr addrspace(1) undef - - %fortytwo.seven = call float @llvm.amdgcn.ldexp.f32(float 42.0, i32 73) - store volatile float %fortytwo.seven, ptr addrspace(1) undef - - ret void -} - -; Technically we should probably flush these depending on the expected -; denormal mode of the function, but no other IR constant folding -; considers this. -define void @ldexp_f32_denormal() { -; CHECK-LABEL: @ldexp_f32_denormal( -; CHECK-NEXT: store volatile float 0x380FFFFFC0000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: store volatile float 0x381FFFFFC0000000, ptr addrspace(1) undef, align 4 -; CHECK-NEXT: ret void -; - %denormal.0 = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 0) - store volatile float %denormal.0, ptr addrspace(1) undef - - %denormal.1 = call float @llvm.amdgcn.ldexp.f32(float 0x380FFFFFC0000000, i32 1) - store volatile float %denormal.1, ptr addrspace(1) undef - - ret void -} - -define void @ldexp_f64() { -; CHECK-LABEL: @ldexp_f64( -; CHECK-NEXT: store volatile double 2.000000e+00, ptr addrspace(1) undef, align 8 -; CHECK-NEXT: store volatile double 4.000000e+00, ptr addrspace(1) undef, align 8 -; CHECK-NEXT: store volatile double 0x44D5000000000000, ptr addrspace(1) undef, align 8 -; CHECK-NEXT: ret void -; - %one.one = call double @llvm.amdgcn.ldexp.f64(double 1.0, i32 1) - store volatile double %one.one, ptr addrspace(1) undef - - %one.two = call double @llvm.amdgcn.ldexp.f64(double 1.0, i32 2) - store volatile double %one.two, ptr addrspace(1) undef - - %fortytwo.seven = call double @llvm.amdgcn.ldexp.f64(double 42.0, i32 73) - store volatile double %fortytwo.seven, ptr addrspace(1) undef - - ret void -} - -define void @ldexp_f16() { -; CHECK-LABEL: @ldexp_f16( -; CHECK-NEXT: store volatile half 0xH4000, ptr addrspace(1) undef, align 2 -; CHECK-NEXT: store volatile half 0xH4400, ptr addrspace(1) undef, align 2 -; CHECK-NEXT: store volatile half 0xH7C00, ptr addrspace(1) undef, align 2 -; CHECK-NEXT: ret void -; - %one.one = call half @llvm.amdgcn.ldexp.f16(half 1.0, i32 1) - store volatile half %one.one, ptr addrspace(1) undef - - %one.two = call half @llvm.amdgcn.ldexp.f16(half 1.0, i32 2) - store volatile half %one.two, ptr addrspace(1) undef - - %fortytwo.seven = call half @llvm.amdgcn.ldexp.f16(half 42.0, i32 73) - store volatile half %fortytwo.seven, ptr addrspace(1) undef - - ret void -} - -declare half @llvm.amdgcn.ldexp.f16(half, i32) #1 -declare float @llvm.amdgcn.ldexp.f32(float, i32) #1 -declare double @llvm.amdgcn.ldexp.f64(double, i32) #1 - -attributes #0 = { strictfp } -attributes #1 = { nounwind readnone speculatable } Index: llvm/test/Transforms/InstCombine/ldexp.ll =================================================================== --- llvm/test/Transforms/InstCombine/ldexp.ll +++ llvm/test/Transforms/InstCombine/ldexp.ll @@ -443,8 +443,7 @@ define float @ldexp_ldexp_opposite_constants(float %x) { ; CHECK-LABEL: define float @ldexp_ldexp_opposite_constants ; CHECK-SAME: (float [[X:%.*]]) { -; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 0) -; CHECK-NEXT: ret float [[LDEXP1]] +; CHECK-NEXT: ret float [[X]] ; %ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 8) %ldexp1 = call reassoc float @llvm.ldexp.f32.i32(float %ldexp0, i32 -8) @@ -454,8 +453,7 @@ define float @ldexp_ldexp_negated_variable_reassoc(float %x, i32 %a) { ; CHECK-LABEL: define float @ldexp_ldexp_negated_variable_reassoc ; CHECK-SAME: (float [[X:%.*]], i32 [[A:%.*]]) { -; CHECK-NEXT: [[LDEXP1:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X]], i32 0) -; CHECK-NEXT: ret float [[LDEXP1]] +; CHECK-NEXT: ret float [[X]] ; %ldexp0 = call reassoc float @llvm.ldexp.f32.i32(float %x, i32 %a) %neg.a = sub i32 0, %a @@ -583,8 +581,7 @@ define float @ldexp_ldexp_0(float %x, i32 %y) { ; CHECK-LABEL: define float @ldexp_ldexp_0 ; CHECK-SAME: (float [[X:%.*]], i32 [[Y:%.*]]) { -; CHECK-NEXT: [[LDEXP0:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 0) -; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[LDEXP0]], i32 [[Y]]) +; CHECK-NEXT: [[LDEXP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]]) ; CHECK-NEXT: ret float [[LDEXP1]] ; %ldexp0 = call float @llvm.ldexp.f32.i32(float %x, i32 0) Index: llvm/test/Transforms/InstSimplify/ldexp.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstSimplify/ldexp.ll @@ -0,0 +1,446 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=instsimplify < %s | FileCheck %s + +define float @ldexp_f32_undef_undef() { +; CHECK-LABEL: @ldexp_f32_undef_undef( +; CHECK-NEXT: ret float 0x7FF8000000000000 +; + %call = call float @llvm.ldexp.f32.i32(float undef, i32 undef) + ret float %call +} + +define float @ldexp_f32_poison_undef() { +; CHECK-LABEL: @ldexp_f32_poison_undef( +; CHECK-NEXT: ret float poison +; + %call = call float @llvm.ldexp.f32.i32(float poison, i32 undef) + ret float %call +} + +define float @ldexp_f32_undef_poison() { +; CHECK-LABEL: @ldexp_f32_undef_poison( +; CHECK-NEXT: ret float undef +; + %call = call float @llvm.ldexp.f32.i32(float undef, i32 poison) + ret float %call +} + +define float @ldexp_f32_poison_poison() { +; CHECK-LABEL: @ldexp_f32_poison_poison( +; CHECK-NEXT: ret float poison +; + %call = call float @llvm.ldexp.f32.i32(float poison, i32 poison) + ret float %call +} + +; If the exponent is 0, it doesn't matter if the first argument is +; constant or not. +define void @ldexp_f32_exp0(float %x) { +; CHECK-LABEL: @ldexp_f32_exp0( +; CHECK-NEXT: store volatile float [[X:%.*]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float [[X]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[ONE:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 1) +; CHECK-NEXT: store volatile float [[ONE]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %zero = call float @llvm.ldexp.f32.i32(float %x, i32 0) + store volatile float %zero, ptr addrspace(1) undef + + %undef = call float @llvm.ldexp.f32.i32(float %x, i32 undef) + store volatile float %undef, ptr addrspace(1) undef + + %one = call float @llvm.ldexp.f32.i32(float %x, i32 1) + store volatile float %one, ptr addrspace(1) undef + ret void +} + +define void @ldexp_v2f32_exp0(<2 x float> %x) { +; CHECK-LABEL: @ldexp_v2f32_exp0( +; CHECK-NEXT: store volatile <2 x float> [[X:%.*]], ptr addrspace(1) undef, align 8 +; CHECK-NEXT: store volatile <2 x float> [[X]], ptr addrspace(1) undef, align 8 +; CHECK-NEXT: store volatile <2 x float> [[X]], ptr addrspace(1) undef, align 8 +; CHECK-NEXT: ret void +; + %part.undef0 = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> %x, <2 x i32> ) + store volatile <2 x float> %part.undef0, ptr addrspace(1) undef + + %part.undef1 = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> %x, <2 x i32> ) + store volatile <2 x float> %part.undef1, ptr addrspace(1) undef + + %zero = call <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> %x, <2 x i32> zeroinitializer) + store volatile <2 x float> %zero, ptr addrspace(1) undef + ret void +} + +; Test variable exponent but zero or undef value. +define void @ldexp_f32_val0(i32 %y) { +; CHECK-LABEL: @ldexp_f32_val0( +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x7FF8000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %zero = call float @llvm.ldexp.f32.i32(float 0.0, i32 %y) + store volatile float %zero, ptr addrspace(1) undef + + %neg.zero = call float @llvm.ldexp.f32.i32(float -0.0, i32 %y) + store volatile float %neg.zero, ptr addrspace(1) undef + + %undef = call float @llvm.ldexp.f32.i32(float undef, i32 %y) + store volatile float %undef, ptr addrspace(1) undef + ret void +} + +define void @ldexp_f32_val_infinity(i32 %y) { +; CHECK-LABEL: @ldexp_f32_val_infinity( +; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %inf = call float @llvm.ldexp.f32.i32(float 0x7ff0000000000000, i32 %y) + store volatile float %inf, ptr addrspace(1) undef + + %neg.inf = call float @llvm.ldexp.f32.i32(float 0xfff0000000000000, i32 %y) + store volatile float %neg.inf, ptr addrspace(1) undef + + %inf.zero = call float @llvm.ldexp.f32.i32(float 0x7ff0000000000000, i32 0) + store volatile float %inf.zero, ptr addrspace(1) undef + + %neg.inf.zero = call float @llvm.ldexp.f32.i32(float 0xfff0000000000000, i32 0) + store volatile float %neg.inf.zero, ptr addrspace(1) undef + + ret void +} + +; Signaling nan should be quieted. +; Technically this depends on the ieee_mode in the mode register. +define void @ldexp_f32_val_nan(i32 %y) { +; CHECK-LABEL: @ldexp_f32_val_nan( +; CHECK-NEXT: store volatile float 0x7FF8001000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xFFF8000100000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x7FF8000020000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xFFFFFFFFE0000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %plus.qnan = call float @llvm.ldexp.f32.i32(float 0x7ff0001000000000, i32 %y) + store volatile float %plus.qnan, ptr addrspace(1) undef + + %neg.qnan = call float @llvm.ldexp.f32.i32(float 0xfff0000100000000, i32 %y) + store volatile float %neg.qnan, ptr addrspace(1) undef + + %plus.snan = call float @llvm.ldexp.f32.i32(float 0x7FF0000020000000, i32 %y) + store volatile float %plus.snan, ptr addrspace(1) undef + + %neg.snan = call float @llvm.ldexp.f32.i32(float 0xFFF7FFFFE0000000, i32 %y) + store volatile float %neg.snan, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f32_val_nan_strictfp(i32 %y) #0 { +; CHECK-LABEL: @ldexp_f32_val_nan_strictfp( +; CHECK-NEXT: [[PLUS_QNAN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0001000000000, i32 [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0:[0-9]+]] +; CHECK-NEXT: store volatile float [[PLUS_QNAN]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[NEG_QNAN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0xFFF0000100000000, i32 [[Y]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[NEG_QNAN]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[PLUS_SNAN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 [[Y]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[PLUS_SNAN]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[NEG_SNAN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0xFFF7FFFFE0000000, i32 [[Y]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[NEG_SNAN]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[UNDEF:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float undef, i32 [[Y]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNDEF]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %plus.qnan = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7ff0001000000000, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %plus.qnan, ptr addrspace(1) undef + + %neg.qnan = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0xfff0000100000000, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %neg.qnan, ptr addrspace(1) undef + + %plus.snan = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %plus.snan, ptr addrspace(1) undef + + %neg.snan = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0xFFF7FFFFE0000000, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %neg.snan, ptr addrspace(1) undef + + %undef = call float @llvm.experimental.constrained.ldexp.f32.i32(float undef, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %undef, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f32_0() { +; CHECK-LABEL: @ldexp_f32_0( +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %zero = call float @llvm.ldexp.f32.i32(float 0.0, i32 0) + store volatile float %zero, ptr addrspace(1) undef + + %neg.zero = call float @llvm.ldexp.f32.i32(float -0.0, i32 0) + store volatile float %neg.zero, ptr addrspace(1) undef + + %one = call float @llvm.ldexp.f32.i32(float 0.0, i32 1) + store volatile float %one, ptr addrspace(1) undef + + %min.exp = call float @llvm.ldexp.f32.i32(float 0.0, i32 -126) + store volatile float %min.exp, ptr addrspace(1) undef + + %min.exp.sub1 = call float @llvm.ldexp.f32.i32(float 0.0, i32 -127) + store volatile float %min.exp.sub1, ptr addrspace(1) undef + + %max.exp = call float @llvm.ldexp.f32.i32(float 0.0, i32 127) + store volatile float %max.exp, ptr addrspace(1) undef + + %max.exp.plus1 = call float @llvm.ldexp.f32.i32(float 0.0, i32 128) + store volatile float %max.exp.plus1, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f32_undef_strictfp(float %x, i32 %y) #0 { +; CHECK-LABEL: @ldexp_f32_undef_strictfp( +; CHECK-NEXT: [[UNDEF_EXP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X:%.*]], i32 undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNDEF_EXP]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float [[X]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[UNDEF_VAL:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float undef, i32 [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNDEF_VAL]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float poison, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float poison, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float undef, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %undef.exp = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %undef.exp, ptr addrspace(1) undef + %poison.exp = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %poison.exp, ptr addrspace(1) undef + %undef.val = call float @llvm.experimental.constrained.ldexp.f32.i32(float undef, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %undef.val, ptr addrspace(1) undef + %poison.val = call float @llvm.experimental.constrained.ldexp.f32.i32(float poison, i32 %y, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %poison.val, ptr addrspace(1) undef + %poison.undef = call float @llvm.experimental.constrained.ldexp.f32.i32(float poison, i32 undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %poison.undef, ptr addrspace(1) undef + %undef.poison = call float @llvm.experimental.constrained.ldexp.f32.i32(float undef, i32 poison, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %undef.poison, ptr addrspace(1) undef + ret void +} + +; Should be able to ignore strictfp in this case +define void @ldexp_f32_0_strictfp(float %x) #0 { +; CHECK-LABEL: @ldexp_f32_0_strictfp( +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[UNKNOWN_ZERO:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X:%.*]], i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNKNOWN_ZERO]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[UNKNOWN_UNDEF:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X]], i32 undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNKNOWN_UNDEF]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[DENORMAL_0:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[DENORMAL_0]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[DENORMAL_1:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 1, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[DENORMAL_1]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %zero = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0.0, i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %zero, ptr addrspace(1) undef + + %neg.zero = call float @llvm.experimental.constrained.ldexp.f32.i32(float -0.0, i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %neg.zero, ptr addrspace(1) undef + + %one = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0.0, i32 1, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %one, ptr addrspace(1) undef + + %unknown.zero = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %unknown.zero, ptr addrspace(1) undef + + %unknown.undef = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 undef, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %unknown.undef, ptr addrspace(1) undef + + %denormal.0 = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 0, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %denormal.0, ptr addrspace(1) undef + + %denormal.1 = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 1, metadata !"round.dynamic", metadata !"fpexcept.maytrap") #0 + store volatile float %denormal.1, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f32() { +; CHECK-LABEL: @ldexp_f32( +; CHECK-NEXT: store volatile float 2.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 4.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 8.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 5.000000e-01, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x3810000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x3800000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x47E0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x7FF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -2.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -4.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -8.000000e+00, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float -5.000000e-01, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xB810000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xB800000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xC7E0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0xFFF0000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x44D5000000000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %one.one = call float @llvm.ldexp.f32.i32(float 1.0, i32 1) + store volatile float %one.one, ptr addrspace(1) undef + + %one.two = call float @llvm.ldexp.f32.i32(float 1.0, i32 2) + store volatile float %one.two, ptr addrspace(1) undef + + %one.three = call float @llvm.ldexp.f32.i32(float 1.0, i32 3) + store volatile float %one.three, ptr addrspace(1) undef + + %one.negone = call float @llvm.ldexp.f32.i32(float 1.0, i32 -1) + store volatile float %one.negone, ptr addrspace(1) undef + + %one.min.exp = call float @llvm.ldexp.f32.i32(float 1.0, i32 -126) + store volatile float %one.min.exp, ptr addrspace(1) undef + + %one.min.exp.sub1 = call float @llvm.ldexp.f32.i32(float 1.0, i32 -127) + store volatile float %one.min.exp.sub1, ptr addrspace(1) undef + + %one.max.exp = call float @llvm.ldexp.f32.i32(float 1.0, i32 127) + store volatile float %one.max.exp, ptr addrspace(1) undef + + %one.max.exp.plus1 = call float @llvm.ldexp.f32.i32(float 1.0, i32 128) + store volatile float %one.max.exp.plus1, ptr addrspace(1) undef + + %neg.one.one = call float @llvm.ldexp.f32.i32(float -1.0, i32 1) + store volatile float %neg.one.one, ptr addrspace(1) undef + + %neg.one.two = call float @llvm.ldexp.f32.i32(float -1.0, i32 2) + store volatile float %neg.one.two, ptr addrspace(1) undef + + %neg.one.three = call float @llvm.ldexp.f32.i32(float -1.0, i32 3) + store volatile float %neg.one.three, ptr addrspace(1) undef + + %neg.one.negone = call float @llvm.ldexp.f32.i32(float -1.0, i32 -1) + store volatile float %neg.one.negone, ptr addrspace(1) undef + + %neg.one.min.exp = call float @llvm.ldexp.f32.i32(float -1.0, i32 -126) + store volatile float %neg.one.min.exp, ptr addrspace(1) undef + + %neg.one.min.exp.sub1 = call float @llvm.ldexp.f32.i32(float -1.0, i32 -127) + store volatile float %neg.one.min.exp.sub1, ptr addrspace(1) undef + + %neg.one.max.exp = call float @llvm.ldexp.f32.i32(float -1.0, i32 127) + store volatile float %neg.one.max.exp, ptr addrspace(1) undef + + %neg.one.max.exp.plus1 = call float @llvm.ldexp.f32.i32(float -1.0, i32 128) + store volatile float %neg.one.max.exp.plus1, ptr addrspace(1) undef + + %fortytwo.seven = call float @llvm.ldexp.f32.i32(float 42.0, i32 73) + store volatile float %fortytwo.seven, ptr addrspace(1) undef + + ret void +} + +; Technically we should probably flush these depending on the expected +; denormal mode of the function, but no other IR constant folding +; considers this. +define void @ldexp_f32_denormal() { +; CHECK-LABEL: @ldexp_f32_denormal( +; CHECK-NEXT: store volatile float 0x380FFFFFC0000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: store volatile float 0x381FFFFFC0000000, ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %denormal.0 = call float @llvm.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 0) + store volatile float %denormal.0, ptr addrspace(1) undef + + %denormal.1 = call float @llvm.ldexp.f32.i32(float 0x380FFFFFC0000000, i32 1) + store volatile float %denormal.1, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f64() { +; CHECK-LABEL: @ldexp_f64( +; CHECK-NEXT: store volatile double 2.000000e+00, ptr addrspace(1) undef, align 8 +; CHECK-NEXT: store volatile double 4.000000e+00, ptr addrspace(1) undef, align 8 +; CHECK-NEXT: store volatile double 0x44D5000000000000, ptr addrspace(1) undef, align 8 +; CHECK-NEXT: ret void +; + %one.one = call double @llvm.ldexp.f64.i32(double 1.0, i32 1) + store volatile double %one.one, ptr addrspace(1) undef + + %one.two = call double @llvm.ldexp.f64.i32(double 1.0, i32 2) + store volatile double %one.two, ptr addrspace(1) undef + + %fortytwo.seven = call double @llvm.ldexp.f64.i32(double 42.0, i32 73) + store volatile double %fortytwo.seven, ptr addrspace(1) undef + + ret void +} + +define void @ldexp_f16() { +; CHECK-LABEL: @ldexp_f16( +; CHECK-NEXT: store volatile half 0xH4000, ptr addrspace(1) undef, align 2 +; CHECK-NEXT: store volatile half 0xH4400, ptr addrspace(1) undef, align 2 +; CHECK-NEXT: store volatile half 0xH7C00, ptr addrspace(1) undef, align 2 +; CHECK-NEXT: ret void +; + %one.one = call half @llvm.ldexp.f16.i32(half 1.0, i32 1) + store volatile half %one.one, ptr addrspace(1) undef + + %one.two = call half @llvm.ldexp.f16.i32(half 1.0, i32 2) + store volatile half %one.two, ptr addrspace(1) undef + + %fortytwo.seven = call half @llvm.ldexp.f16.i32(half 42.0, i32 73) + store volatile half %fortytwo.seven, ptr addrspace(1) undef + + ret void +} + +define void @constant_fold_ldexp_f32_val_strictfp(i32 %y) #0 { +; CHECK-LABEL: @constant_fold_ldexp_f32_val_strictfp( +; CHECK-NEXT: [[SNAN_MAY_TRAP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 3, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[SNAN_MAY_TRAP]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[SNAN_MAY_NOT_TRAP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 3, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[SNAN_MAY_NOT_TRAP]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[UNKNOWN_ROUNDING:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.500000e+00, i32 42, metadata !"round.dynamic", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[UNKNOWN_ROUNDING]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[NORMAL:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.500000e+00, i32 42, metadata !"round.tonearest", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[NORMAL]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: [[NORMAL_DOWN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.500000e+00, i32 42, metadata !"round.downward", metadata !"fpexcept.ignore") #[[ATTR0]] +; CHECK-NEXT: store volatile float [[NORMAL_DOWN]], ptr addrspace(1) undef, align 4 +; CHECK-NEXT: ret void +; + %snan.may.trap = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 3, metadata !"round.tonearest", metadata !"fpexcept.maytrap") #0 + store volatile float %snan.may.trap, ptr addrspace(1) undef + + %snan.may.not.trap = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0000020000000, i32 3, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + store volatile float %snan.may.not.trap, ptr addrspace(1) undef + + %unknown.rounding = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.5, i32 42, metadata !"round.dynamic", metadata !"fpexcept.ignore") #0 + store volatile float %unknown.rounding, ptr addrspace(1) undef + + %normal = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.5, i32 42, metadata !"round.tonearest", metadata !"fpexcept.ignore") #0 + store volatile float %normal, ptr addrspace(1) undef + + %normal.down = call float @llvm.experimental.constrained.ldexp.f32.i32(float 2.5, i32 42, metadata !"round.downward", metadata !"fpexcept.ignore") #0 + store volatile float %normal.down, ptr addrspace(1) undef + + ret void +} + +declare half @llvm.ldexp.f16.i32(half, i32) #1 +declare float @llvm.ldexp.f32.i32(float, i32) #1 +declare double @llvm.ldexp.f64.i32(double, i32) #1 +declare <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float>, <2 x i32>) #1 +declare float @llvm.experimental.constrained.ldexp.f32.i32(float, i32, metadata, metadata) #1 + +attributes #0 = { strictfp } +attributes #1 = { nounwind readnone speculatable }