[llvm] 0006184 - InstSimplify: Simplifications for ldexp

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 12 22:38:55 PDT 2023


Author: Matt Arsenault
Date: 2023-09-13T08:38:48+03:00
New Revision: 00061843bd93b7dd9f83e1448e569e193c22ccf8

URL: https://github.com/llvm/llvm-project/commit/00061843bd93b7dd9f83e1448e569e193c22ccf8
DIFF: https://github.com/llvm/llvm-project/commit/00061843bd93b7dd9f83e1448e569e193c22ccf8.diff

LOG: InstSimplify: Simplifications for ldexp

Ported from old amdgcn intrinsic which will soon be deleted.

https://reviews.llvm.org/D149587

Added: 
    llvm/test/Transforms/InstSimplify/ldexp.ll

Modified: 
    llvm/lib/Analysis/ConstantFolding.cpp
    llvm/lib/Analysis/InstructionSimplify.cpp
    llvm/test/Transforms/InstCombine/ldexp.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index ac846ce42c7080d..f12a5dd2f5522c0 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1548,6 +1548,7 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
   case Intrinsic::cos:
   case Intrinsic::pow:
   case Intrinsic::powi:
+  case Intrinsic::ldexp:
   case Intrinsic::fma:
   case Intrinsic::fmuladd:
   case Intrinsic::frexp:
@@ -2627,6 +2628,11 @@ static Constant *ConstantFoldScalarCall2(StringRef Name,
       }
     } else if (auto *Op2C = dyn_cast<ConstantInt>(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<FPClassTest>(Op2C->getZExtValue());
         bool Result =

diff  --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d0cc56ebc2be319..5fe0d53c313d40e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6094,6 +6094,56 @@ static Value *simplifyRelativeLoad(Constant *Ptr, Constant *Offset,
   return LoadedLHSPtr;
 }
 
+// TODO: Need to pass in FastMathFlags
+static Value *simplifyLdexp(Value *Op0, Value *Op1, const SimplifyQuery &Q,
+                            bool IsStrict) {
+  // ldexp(poison, x) -> poison
+  // ldexp(x, poison) -> poison
+  if (isa<PoisonValue>(Op0) || isa<PoisonValue>(Op1))
+    return Op0;
+
+  // ldexp(undef, x) -> nan
+  if (Q.isUndefValue(Op0))
+    return ConstantFP::getNaN(Op0->getType());
+
+  if (!IsStrict) {
+    // TODO: Could insert a canonicalize for strict
+
+    // 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;
+
+  // These are canonicalization dropping, could do it if we knew how we could
+  // ignore denormal flushes and target handling of nan payload bits.
+  if (IsStrict)
+    return nullptr;
+
+  // TODO: Could quiet this with strictfp if the exception mode isn't strict.
+  if (C && C->isNaN())
+    return ConstantFP::get(Op0->getType(), C->makeQuiet());
+
+  // ldexp(x, 0) -> x
+
+  // TODO: Could fold this if we know the exception mode isn't
+  // strict, we know the denormal mode and other target modes.
+  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.
@@ -6446,6 +6496,8 @@ static Value *simplifyBinaryIntrinsic(Function *F, Value *Op0, Value *Op1,
         return Op0;
     }
     break;
+  case Intrinsic::ldexp:
+    return simplifyLdexp(Op0, Op1, Q, false);
   case Intrinsic::copysign:
     // copysign X, X --> X
     if (Op0 == Op1)
@@ -6709,6 +6761,8 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
                             *FPI->getExceptionBehavior(),
                             *FPI->getRoundingMode());
   }
+  case Intrinsic::experimental_constrained_ldexp:
+    return simplifyLdexp(Args[0], Args[1], Q, true);
   default:
     return nullptr;
   }

diff  --git a/llvm/test/Transforms/InstCombine/ldexp.ll b/llvm/test/Transforms/InstCombine/ldexp.ll
index e1949bcde4260ae..3ede7d913b52571 100644
--- a/llvm/test/Transforms/InstCombine/ldexp.ll
+++ b/llvm/test/Transforms/InstCombine/ldexp.ll
@@ -488,8 +488,7 @@ define float @ldexp_ldexp_constants_nsz1(float %x) {
 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)
@@ -499,8 +498,7 @@ define float @ldexp_ldexp_opposite_constants(float %x) {
 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
@@ -628,8 +626,7 @@ define float @ldexp_reassoc_ldexp_reassoc_0(float %x, i32 %y) {
 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)
@@ -754,8 +751,7 @@ define float @ldexp_neg1(float %x) {
 define float @ldexp_0(float %x) {
 ; CHECK-LABEL: define float @ldexp_0
 ; CHECK-SAME: (float [[X:%.*]]) {
-; CHECK-NEXT:    [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 0)
-; CHECK-NEXT:    ret float [[LDEXP]]
+; CHECK-NEXT:    ret float [[X]]
 ;
   %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 0)
   ret float %ldexp

diff  --git a/llvm/test/Transforms/InstSimplify/ldexp.ll b/llvm/test/Transforms/InstSimplify/ldexp.ll
new file mode 100644
index 000000000000000..c6bb0141199f217
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/ldexp.ll
@@ -0,0 +1,505 @@
+; 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> <i32 0, i32 undef>)
+  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> <i32 undef, i32 0>)
+  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_maytrap(i32 %y) #0 {
+; CHECK-LABEL: @ldexp_f32_val_nan_strictfp_maytrap(
+; 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:    store volatile float 0x7FF8000000000000, 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_val_nan_strictfp_strict(i32 %y) #0 {
+; CHECK-LABEL: @ldexp_f32_val_nan_strictfp_strict(
+; CHECK-NEXT:    [[PLUS_QNAN:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float 0x7FF0001000000000, i32 [[Y:%.*]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]]
+; 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.strict") #[[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.strict") #[[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.strict") #[[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.strict") #[[ATTR0]]
+; CHECK-NEXT:    store volatile float 0x7FF8000000000000, 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.strict") #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.strict") #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.strict") #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.strict") #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.strict") #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:    store volatile float 0x7FF8000000000000, 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 @ldexp_ppcf128() {
+; CHECK-LABEL: @ldexp_ppcf128(
+; CHECK-NEXT:    store volatile ppc_fp128 0xMFFF00000000000000000000000000000, ptr addrspace(1) undef, align 16
+; CHECK-NEXT:    store volatile ppc_fp128 0xMFFFC0000000000000000000000000000, ptr addrspace(1) undef, align 16
+; CHECK-NEXT:    store volatile ppc_fp128 0xM3FD00000000000000000000000000000, ptr addrspace(1) undef, align 16
+; CHECK-NEXT:    store volatile ppc_fp128 0xM41700000000000000000000000000000, ptr addrspace(1) undef, align 16
+; CHECK-NEXT:    store volatile ppc_fp128 0xMC0700000000000000000000000000000, ptr addrspace(1) undef, align 16
+; CHECK-NEXT:    ret void
+;
+  %neginf = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 0xMFFF00000000000000000000000000000, i32 0)
+  store volatile ppc_fp128 %neginf, ptr addrspace(1) undef
+
+  %snan = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 0xMFFFC0000000000000000000000000000, i32 0)
+  store volatile ppc_fp128 %snan, ptr addrspace(1) undef
+
+  %one.neg2 = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 0xM3FF00000000000000000000000000000, i32 -2)
+  store volatile ppc_fp128 %one.neg2, ptr addrspace(1) undef
+
+  %one.24 = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 0xM3FF00000000000000000000000000000, i32 24)
+  store volatile ppc_fp128 %one.24, ptr addrspace(1) undef
+
+  %negone.8 = call ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128 0xMBFF00000000000000000000000000000, i32 8)
+  store volatile ppc_fp128 %negone.8, 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
+declare ppc_fp128 @llvm.ldexp.ppcf128.i32(ppc_fp128, i32) #1
+
+attributes #0 = { strictfp }
+attributes #1 = { nounwind readnone speculatable }


        


More information about the llvm-commits mailing list