[llvm] [APFloat][SelectionDAG] Support Float8E5M3FNU in `convert.{to,from}.arbitrary.fp` (PR #216387)

Chinmay Deshpande via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 11:17:55 PDT 2026


https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/216387

>From a479bce38b6f5dba0acd38c6b23ffd18ce1c667b Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Sat, 15 Aug 2026 14:32:28 -0400
Subject: [PATCH 1/2] [APFloat][NFC] Add unit test coverage for
 getArbitraryFPSemantics

isValidArbitraryFPFormat and getArbitraryFPFormatSizeInBits have unit
tests, but getArbitraryFPSemantics, the mapping the conversion
intrinsics actually lower through, had none. Cover the formats it
supports, the valid formats it does not support yet, and invalid format
strings, and check that the two tables agree on the size of every
format with lowerable semantics.

Also add the missing Float8E5M3FNU case to the
getArbitraryFPFormatSizeInBits test.

Change-Id: I8fa9580ca6f03fd5e7b1de5354b71283cebb0f95
---
 llvm/unittests/ADT/APFloatTest.cpp | 45 ++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 4ef2814e1a7b3..0c5103c4b49e4 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -10765,6 +10765,7 @@ TEST(APFloatTest, getArbitraryFPFormatSizeInBits) {
   EXPECT_EQ(8u, APFloat::getArbitraryFPFormatSizeInBits("Float8E4M3B11FNUZ"));
   EXPECT_EQ(8u, APFloat::getArbitraryFPFormatSizeInBits("Float8E3M4"));
   EXPECT_EQ(8u, APFloat::getArbitraryFPFormatSizeInBits("Float8E8M0FNU"));
+  EXPECT_EQ(8u, APFloat::getArbitraryFPFormatSizeInBits("Float8E5M3FNU"));
   EXPECT_EQ(6u, APFloat::getArbitraryFPFormatSizeInBits("Float6E3M2FN"));
   EXPECT_EQ(6u, APFloat::getArbitraryFPFormatSizeInBits("Float6E2M3FN"));
   EXPECT_EQ(4u, APFloat::getArbitraryFPFormatSizeInBits("Float4E2M1FN"));
@@ -10776,6 +10777,50 @@ TEST(APFloatTest, getArbitraryFPFormatSizeInBits) {
   EXPECT_EQ(0u, APFloat::getArbitraryFPFormatSizeInBits("unknown"));
 }
 
+TEST(APFloatTest, getArbitraryFPSemantics) {
+  // Formats that can currently be lowered map to their semantics.
+  EXPECT_EQ(&APFloat::Float8E5M2(),
+            APFloat::getArbitraryFPSemantics("Float8E5M2"));
+  EXPECT_EQ(&APFloat::Float8E4M3FN(),
+            APFloat::getArbitraryFPSemantics("Float8E4M3FN"));
+  EXPECT_EQ(&APFloat::Float6E3M2FN(),
+            APFloat::getArbitraryFPSemantics("Float6E3M2FN"));
+  EXPECT_EQ(&APFloat::Float6E2M3FN(),
+            APFloat::getArbitraryFPSemantics("Float6E2M3FN"));
+  EXPECT_EQ(&APFloat::Float4E2M1FN(),
+            APFloat::getArbitraryFPSemantics("Float4E2M1FN"));
+
+  // Formats that are valid but cannot be lowered yet report no semantics.
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E5M2FNUZ"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E4M3"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E4M3FNUZ"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E4M3B11FNUZ"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E3M4"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E8M0FNU"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E5M3FNU"));
+
+  // Invalid formats report no semantics.
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics(""));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("float8e5m2"));
+  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("unknown"));
+}
+
+// The two arbitrary FP format tables must agree: every format with lowerable
+// semantics reports the size of those semantics.
+TEST(APFloatTest, ArbitraryFPSemanticsMatchSizeInBits) {
+  for (StringRef Format :
+       {"Float8E5M2", "Float8E5M2FNUZ", "Float8E4M3", "Float8E4M3FN",
+        "Float8E4M3FNUZ", "Float8E4M3B11FNUZ", "Float8E3M4", "Float8E8M0FNU",
+        "Float8E5M3FNU", "Float6E3M2FN", "Float6E2M3FN", "Float4E2M1FN"}) {
+    ASSERT_TRUE(APFloat::isValidArbitraryFPFormat(Format)) << Format;
+    if (const fltSemantics *Sem = APFloat::getArbitraryFPSemantics(Format))
+      EXPECT_EQ(APFloat::getSizeInBits(*Sem),
+                APFloat::getArbitraryFPFormatSizeInBits(Format))
+          << Format;
+  }
+}
+
 TEST(APFloatTest, DecimalStringPreservesInexactStatus) {
   APFloat F(APFloat::IEEEsingle());
 

>From b2696f6959bd33999996d1fb386a7567d7cf8a83 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Fri, 14 Aug 2026 14:32:38 -0400
Subject: [PATCH 2/2] [APFloat][SelectionDAG] Support Float8E5M3FNU in
 convert.{to,from}.arbitrary.fp

Float8E5M3FNU was already accepted by the IR verifier, because
isValidArbitraryFPFormat is defined in terms of
getArbitraryFPFormatSizeInBits and that table covers it. It was missing
from getArbitraryFPSemantics, so SelectionDAGBuilder rejected
it with "not implemented format" and the verifier-clean IR failed to
compile. Add the mapping and the corresponding entries in the
expandCONVERT_{TO,FROM}_ARBITRARY_FP format allowlists.

Unlike every other format the expansions handle so far, Float8E5M3FNU is
unsigned: it has no sign bit, so all 8 bits go to a 5-bit exponent and a
3-bit significand.

Since an unsigned format cannot represent a negative value, a negative
input now saturates to zero when the saturate flag is set, and is poison
otherwise. -0.0 is excluded from that and still converts to +0, and the
check is ordered before the NaN case so a negative NaN still produces the
NaN encoding. APFloat treats constructing a negative value in an unsigned
semantics as unreachable, so there was no existing behaviour to match.

Also list the format in the LangRef tables of supported interpretations
and document that a negative input cannot be encoded by an unsigned
format.

Change-Id: Ibaeacdb9e20b2ac6d08da80b824ec439f2ba8615
---
 llvm/docs/LangRef.md                          |   9 +-
 .../CodeGen/SelectionDAG/TargetLowering.cpp   |  48 +++-
 llvm/lib/Support/APFloat.cpp                  |   4 +-
 .../AMDGPU/float-to-arbitrary-fp-e5m3fnu.ll   | 263 ++++++++++++++++++
 .../test/CodeGen/X86/float-to-arbitrary-fp.ll | 189 +++++++++++++
 llvm/unittests/ADT/APFloatTest.cpp            |   3 +-
 6 files changed, 500 insertions(+), 16 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/float-to-arbitrary-fp-e5m3fnu.ll

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index b36b7c052646a..3d7917c4a72cc 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -21511,7 +21511,7 @@ type and first argument.
 
   - FP8 formats: `"Float8E5M2"`, `"Float8E5M2FNUZ"`, `"Float8E4M3"`,
     `"Float8E4M3FN"`, `"Float8E4M3FNUZ"`, `"Float8E4M3B11FNUZ"`, `"Float8E3M4"`,
-    `"Float8E8M0FNU"`
+    `"Float8E8M0FNU"`, `"Float8E5M3FNU"`
   - FP6 formats: `"Float6E3M2FN"`, `"Float6E2M3FN"`
   - FP4 formats: `"Float4E2M1FN"`
 
@@ -21564,6 +21564,11 @@ integer whose bit width equals the format's bit width (`i8` for FP8, `i6` for FP
   - When `saturation` is `false` and the target format does not support infinity (e.g., formats
     with "FN" suffix), the intrinsic returns a poison value.
   - When `saturation` is `true`, the value is clamped to the maximum/minimum representable finite value.
+- **Negative values in unsigned formats**: Formats without a sign bit (for example
+  `"Float8E5M3FNU"`) cannot represent negative values. `-0.0` converts to zero. Any other negative
+  input is clamped to the minimum representable finite value when `saturation` is `true`, and
+  returns a poison value otherwise. A negative NaN still converts to the NaN encoding when the
+  format supports NaN.
 
 For FP6/FP4 interpretations, producers are expected to use `saturation` = `true`; using `saturation` = `false` and generating NaN/Inf/overflowing values results in a poison value.
 
@@ -21609,7 +21614,7 @@ overloaded on both its return type and first argument.
 
   - FP8 formats: `"Float8E5M2"`, `"Float8E5M2FNUZ"`, `"Float8E4M3"`,
     `"Float8E4M3FN"`, `"Float8E4M3FNUZ"`, `"Float8E4M3B11FNUZ"`, `"Float8E3M4"`,
-    `"Float8E8M0FNU"`
+    `"Float8E8M0FNU"`, `"Float8E5M3FNU"`
   - FP6 formats: `"Float6E3M2FN"`, `"Float6E2M3FN"`
   - FP4 formats: `"Float4E2M1FN"`
 
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index af93a2a3d2388..927d0724be22f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -9341,6 +9341,7 @@ SDValue TargetLowering::expandCONVERT_TO_ARBITRARY_FP(SDNode *Node,
   switch (Sem) {
   case APFloatBase::S_Float8E5M2:
   case APFloatBase::S_Float8E4M3FN:
+  case APFloatBase::S_Float8E5M3FNU:
   case APFloatBase::S_Float6E3M2FN:
   case APFloatBase::S_Float6E2M3FN:
   case APFloatBase::S_Float4E2M1FN:
@@ -9372,7 +9373,9 @@ SDValue TargetLowering::expandCONVERT_TO_ARBITRARY_FP(SDNode *Node,
   const unsigned DstBits = APFloat::getSizeInBits(DstSem);
   const unsigned DstPrecision = APFloat::semanticsPrecision(DstSem);
   const unsigned DstMant = DstPrecision - 1;
-  const unsigned DstExpBits = DstBits - DstMant - 1;
+  // Unsigned formats such as Float8E5M3FNU spend no bit on the sign.
+  const bool DstHasSign = APFloat::semanticsHasSignedRepr(DstSem);
+  const unsigned DstExpBits = DstBits - (DstHasSign ? 1 : 0) - DstMant;
   const int DstBias = 1 - APFloat::semanticsMinExponent(DstSem);
   const unsigned DstExpMax = (1U << DstExpBits) - 1;
   const uint64_t DstMantMask = (DstMant > 0) ? ((1ULL << DstMant) - 1) : 0;
@@ -9545,10 +9548,14 @@ SDValue TargetLowering::expandCONVERT_TO_ARBITRARY_FP(SDNode *Node,
       DAG.getNode(ISD::ADD, dl, IntVT, NewExp,
                   DAG.getNode(ISD::ZERO_EXTEND, dl, IntVT, MantOverflow));
 
-  // Precompute sign shifted to MSB of destination.
+  // Precompute sign shifted to MSB of destination. Unsigned formats have no
+  // sign bit, so nothing is merged in and negative inputs are resolved
+  // separately when assembling the final result.
   SDValue SignShifted =
-      DAG.getNode(ISD::SHL, dl, IntVT, SignBit,
-                  DAG.getShiftAmountConstant(DstBits - 1, IntVT, dl));
+      DstHasSign
+          ? DAG.getNode(ISD::SHL, dl, IntVT, SignBit,
+                        DAG.getShiftAmountConstant(DstBits - 1, IntVT, dl))
+          : Zero;
 
   // Destination denormal conversion (when new_exp <= 0).
   // Shift the mantissa right by 1 - new_exp additional bits and set the
@@ -9731,6 +9738,19 @@ SDValue TargetLowering::expandCONVERT_TO_ARBITRARY_FP(SDNode *Node,
   SDValue Result = FiniteResult;
   Result = DAG.getSelect(dl, IntVT, IsZero, ZeroResult, Result);
   Result = DAG.getSelect(dl, IntVT, IsInf, InfResult, Result);
+
+  // An unsigned destination format cannot encode a negative value. -0.0 is
+  // already covered by the IsZero case above and converts to +0; any other
+  // negative input saturates to zero, or is poison when not saturating.
+  // Applied before the NaN case so that a negative NaN still produces the NaN
+  // encoding.
+  if (!DstHasSign) {
+    SDValue IsNegative =
+        DAG.getSetCC(dl, FPSetCCVT, FloatVal, FPZero, ISD::SETOLT);
+    SDValue NegResult = Saturate ? Zero : DAG.getPOISON(IntVT);
+    Result = DAG.getSelect(dl, IntVT, IsNegative, NegResult, Result);
+  }
+
   Result = DAG.getSelect(dl, IntVT, IsNaN, NaNResult, Result);
 
   // Truncate to destination integer type.
@@ -9752,6 +9772,7 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
   switch (Sem) {
   case APFloatBase::S_Float8E5M2:
   case APFloatBase::S_Float8E4M3FN:
+  case APFloatBase::S_Float8E5M3FNU:
   case APFloatBase::S_Float6E3M2FN:
   case APFloatBase::S_Float6E2M3FN:
   case APFloatBase::S_Float4E2M1FN:
@@ -9767,7 +9788,9 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
   const unsigned SrcBits = APFloat::getSizeInBits(SrcSem);
   const unsigned SrcPrecision = APFloat::semanticsPrecision(SrcSem);
   const unsigned SrcMant = SrcPrecision - 1;
-  const unsigned SrcExp = SrcBits - SrcMant - 1;
+  // Unsigned formats such as Float8E5M3FNU spend no bit on the sign.
+  const bool SrcHasSign = APFloat::semanticsHasSignedRepr(SrcSem);
+  const unsigned SrcExp = SrcBits - (SrcHasSign ? 1 : 0) - SrcMant;
   const int SrcBias = 1 - APFloat::semanticsMinExponent(SrcSem);
   const fltNonfiniteBehavior NFBehavior = SrcSem.nonFiniteBehavior;
 
@@ -9808,13 +9831,16 @@ TargetLowering::expandCONVERT_FROM_ARBITRARY_FP(SDNode *Node,
                               DAG.getShiftAmountConstant(SrcMant, IntVT, dl)),
                   DAG.getConstant(ExpMask, dl, IntVT));
 
-  SDValue SignBit =
-      DAG.getNode(ISD::SRL, dl, IntVT, Src,
-                  DAG.getShiftAmountConstant(SrcBits - 1, IntVT, dl));
-
+  // An unsigned source has no sign bit; bit SrcBits - 1 belongs to the
+  // exponent field, so the result is always non-negative.
   SDValue SignShifted =
-      DAG.getNode(ISD::SHL, dl, IntVT, SignBit,
-                  DAG.getShiftAmountConstant(DstBits - 1, IntVT, dl));
+      SrcHasSign
+          ? DAG.getNode(
+                ISD::SHL, dl, IntVT,
+                DAG.getNode(ISD::SRL, dl, IntVT, Src,
+                            DAG.getShiftAmountConstant(SrcBits - 1, IntVT, dl)),
+                DAG.getShiftAmountConstant(DstBits - 1, IntVT, dl))
+          : Zero;
 
   // Classify the input.
   SDValue ExpAllOnes = DAG.getConstant(ExpMask, dl, IntVT);
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 63007d88ed4d4..21b398e77a64b 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -6054,11 +6054,11 @@ bool APFloatBase::isValidArbitraryFPFormat(StringRef Format) {
 
 const fltSemantics *APFloatBase::getArbitraryFPSemantics(StringRef Format) {
   // TODO: extend to remaining arbitrary FP types: Float8E4M3, Float8E3M4,
-  // Float8E5M2FNUZ, Float8E4M3FNUZ, Float8E4M3B11FNUZ, Float8E8M0FNU,
-  // Float8E5M3FNU.
+  // Float8E5M2FNUZ, Float8E4M3FNUZ, Float8E4M3B11FNUZ, Float8E8M0FNU.
   return StringSwitch<const fltSemantics *>(Format)
       .Case("Float8E5M2", &semFloat8E5M2)
       .Case("Float8E4M3FN", &semFloat8E4M3FN)
+      .Case("Float8E5M3FNU", &semFloat8E5M3FNU)
       .Case("Float4E2M1FN", &semFloat4E2M1FN)
       .Case("Float6E3M2FN", &semFloat6E3M2FN)
       .Case("Float6E2M3FN", &semFloat6E2M3FN)
diff --git a/llvm/test/CodeGen/AMDGPU/float-to-arbitrary-fp-e5m3fnu.ll b/llvm/test/CodeGen/AMDGPU/float-to-arbitrary-fp-e5m3fnu.ll
new file mode 100644
index 0000000000000..2584e5b9fd119
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/float-to-arbitrary-fp-e5m3fnu.ll
@@ -0,0 +1,263 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=amdgpu10.30-unknown-amdhsa < %s | FileCheck %s
+
+; Generic expansion of llvm.convert.{to,from}.arbitrary.fp for Float8E5M3FNU.
+; gfx1030 has no fp8 conversion hardware, so this exercises
+; TargetLowering::expand{CONVERT_TO,CONVERT_FROM}_ARBITRARY_FP.
+
+define i8 @to_e5m3fnu_1_0() {
+; CHECK-LABEL: to_e5m3fnu_1_0:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x78
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 1.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+define i8 @to_e5m3fnu_1_5() {
+; CHECK-LABEL: to_e5m3fnu_1_5:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x7c
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 1.5, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+define i8 @to_e5m3fnu_2_0() {
+; CHECK-LABEL: to_e5m3fnu_2_0:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x80
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 2.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+; Largest finite value: exp=31, mant=6.
+define i8 @to_e5m3fnu_max_finite() {
+; CHECK-LABEL: to_e5m3fnu_max_finite:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0xfe
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 114688.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+; 2^-15 is denormal in this format: exp=0, mant=4.
+define i8 @to_e5m3fnu_denormal() {
+; CHECK-LABEL: to_e5m3fnu_denormal:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 4
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 0x3F00000000000000, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+define i8 @to_e5m3fnu_pos_zero() {
+; CHECK-LABEL: to_e5m3fnu_pos_zero:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 0.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+; -0.0 is representable as +0 even without saturation.
+define i8 @to_e5m3fnu_neg_zero() {
+; CHECK-LABEL: to_e5m3fnu_neg_zero:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float -0.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+define i8 @to_e5m3fnu_nan() {
+; CHECK-LABEL: to_e5m3fnu_nan:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0xff
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 0x7FF8000000000000, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+; The format has no Inf, so a saturating conversion clamps to max finite.
+define i8 @to_e5m3fnu_inf_saturate() {
+; CHECK-LABEL: to_e5m3fnu_inf_saturate:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0xfe
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float 0x7FF0000000000000, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 true)
+  ret i8 %r
+}
+
+; Negative values are unrepresentable; saturating clamps them to zero.
+define i8 @to_e5m3fnu_negative_saturate() {
+; CHECK-LABEL: to_e5m3fnu_negative_saturate:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float -1.0, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 true)
+  ret i8 %r
+}
+
+define i8 @to_e5m3fnu_dynamic(float %x) {
+; CHECK-LABEL: to_e5m3fnu_dynamic:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_frexp_exp_i32_f32_e32 v1, v0
+; CHECK-NEXT:    v_frexp_mant_f32_e32 v3, v0
+; CHECK-NEXT:    s_mov_b32 s4, 0x7fffff
+; CHECK-NEXT:    v_sub_nc_u32_e32 v2, 7, v1
+; CHECK-NEXT:    v_and_or_b32 v4, v3, s4, 0x800000
+; CHECK-NEXT:    v_and_b32_e32 v8, 0x7ffff, v3
+; CHECK-NEXT:    v_bfe_u32 v9, v3, 20, 3
+; CHECK-NEXT:    v_lshrrev_b32_e32 v3, 19, v3
+; CHECK-NEXT:    v_min_u32_e32 v2, 31, v2
+; CHECK-NEXT:    v_sub_nc_u32_e64 v5, v2, 1 clamp
+; CHECK-NEXT:    v_lshrrev_b32_e32 v7, v2, v4
+; CHECK-NEXT:    v_bfe_u32 v6, v4, 0, v5
+; CHECK-NEXT:    v_lshrrev_b32_e32 v4, v5, v4
+; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v6
+; CHECK-NEXT:    v_cndmask_b32_e64 v6, 0, 1, vcc_lo
+; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v8
+; CHECK-NEXT:    v_and_or_b32 v5, v7, 1, v6
+; CHECK-NEXT:    v_cndmask_b32_e64 v6, 0, 1, vcc_lo
+; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v2
+; CHECK-NEXT:    v_and_b32_e32 v4, v4, v5
+; CHECK-NEXT:    v_and_or_b32 v5, v9, 1, v6
+; CHECK-NEXT:    v_cndmask_b32_e32 v2, 0, v4, vcc_lo
+; CHECK-NEXT:    v_and_b32_e32 v3, v3, v5
+; CHECK-NEXT:    v_add_nc_u32_e32 v2, v7, v2
+; CHECK-NEXT:    v_add_nc_u32_e32 v3, v9, v3
+; CHECK-NEXT:    v_cmp_lt_i32_e32 vcc_lo, 7, v2
+; CHECK-NEXT:    v_cmp_lt_i32_e64 s4, 7, v3
+; CHECK-NEXT:    v_cndmask_b32_e64 v2, v2, 0, vcc_lo
+; CHECK-NEXT:    v_cndmask_b32_e64 v3, v3, 0, s4
+; CHECK-NEXT:    v_cndmask_b32_e64 v4, 0, 8, vcc_lo
+; CHECK-NEXT:    v_add_co_ci_u32_e64 v1, null, 14, v1, s4
+; CHECK-NEXT:    v_or_b32_e32 v2, v4, v2
+; CHECK-NEXT:    v_lshl_or_b32 v3, v1, 3, v3
+; CHECK-NEXT:    v_cmp_gt_i32_e32 vcc_lo, 1, v1
+; CHECK-NEXT:    v_cndmask_b32_e32 v1, v3, v2, vcc_lo
+; CHECK-NEXT:    v_cmp_neq_f32_e32 vcc_lo, 0, v0
+; CHECK-NEXT:    v_cndmask_b32_e32 v1, 0, v1, vcc_lo
+; CHECK-NEXT:    v_cmp_o_f32_e32 vcc_lo, v0, v0
+; CHECK-NEXT:    v_cndmask_b32_e32 v0, 0xff, v1, vcc_lo
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %x, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+define float @from_e5m3fnu_1_0() {
+; CHECK-LABEL: from_e5m3fnu_1_0:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 1.0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 120, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_1_5() {
+; CHECK-LABEL: from_e5m3fnu_1_5:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x3fc00000
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 124, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_2_0() {
+; CHECK-LABEL: from_e5m3fnu_2_0:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 2.0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 128, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_max_finite() {
+; CHECK-LABEL: from_e5m3fnu_max_finite:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x47e00000
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 254, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_denormal() {
+; CHECK-LABEL: from_e5m3fnu_denormal:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_bfrev_b32_e32 v0, 28
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 4, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_nan() {
+; CHECK-LABEL: from_e5m3fnu_nan:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0x7fc00000
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 255, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_zero() {
+; CHECK-LABEL: from_e5m3fnu_zero:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_mov_b32_e32 v0, 0
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 0, metadata !"Float8E5M3FNU")
+  ret float %r
+}
+
+define float @from_e5m3fnu_dynamic(i8 %x) {
+; CHECK-LABEL: from_e5m3fnu_dynamic:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_and_b32_e32 v0, 0xffff, v0
+; CHECK-NEXT:    v_and_b32_e32 v1, 7, v0
+; CHECK-NEXT:    v_bfe_u32 v0, v0, 3, 5
+; CHECK-NEXT:    v_ffbh_u32_e32 v2, v1
+; CHECK-NEXT:    v_lshlrev_b32_e32 v5, 20, v1
+; CHECK-NEXT:    v_cmp_ne_u32_e32 vcc_lo, 0, v1
+; CHECK-NEXT:    v_cmp_eq_u32_e64 s4, 0, v0
+; CHECK-NEXT:    v_sub_nc_u32_e32 v3, 31, v2
+; CHECK-NEXT:    v_add_nc_u32_e32 v4, -8, v2
+; CHECK-NEXT:    v_sub_nc_u32_e32 v2, 0x8d, v2
+; CHECK-NEXT:    s_and_b32 vcc_lo, s4, vcc_lo
+; CHECK-NEXT:    v_cmp_eq_u32_e64 s4, 31, v0
+; CHECK-NEXT:    v_lshlrev_b32_e64 v3, v3, 1
+; CHECK-NEXT:    v_xor_b32_e32 v3, v1, v3
+; CHECK-NEXT:    v_lshlrev_b32_e32 v3, v4, v3
+; CHECK-NEXT:    v_lshl_or_b32 v4, v0, 23, v5
+; CHECK-NEXT:    v_lshl_or_b32 v2, v2, 23, v3
+; CHECK-NEXT:    v_add_nc_u32_e32 v3, 0x38000000, v4
+; CHECK-NEXT:    v_or_b32_e32 v4, v0, v1
+; CHECK-NEXT:    v_cndmask_b32_e32 v2, v3, v2, vcc_lo
+; CHECK-NEXT:    v_cmp_ne_u32_e64 s5, 0, v4
+; CHECK-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 7, v1
+; CHECK-NEXT:    v_cndmask_b32_e64 v0, 0, v2, s5
+; CHECK-NEXT:    s_and_b32 s4, s4, vcc_lo
+; CHECK-NEXT:    v_cndmask_b32_e64 v0, v0, 0x7fc00000, s4
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %r = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E5M3FNU")
+  ret float %r
+}
diff --git a/llvm/test/CodeGen/X86/float-to-arbitrary-fp.ll b/llvm/test/CodeGen/X86/float-to-arbitrary-fp.ll
index 8076f0a11762c..7b01ed05706d3 100644
--- a/llvm/test/CodeGen/X86/float-to-arbitrary-fp.ll
+++ b/llvm/test/CodeGen/X86/float-to-arbitrary-fp.ll
@@ -2406,3 +2406,192 @@ define i8 @to_f8e5m2_from_f64(double %x) {
   %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double %x, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
   ret i8 %r
 }
+
+; Float8E5M3FNU
+; Layout: exp(5) mant(3), bias=15, no sign bit
+; Supports: NaN, denormals. No Inf, no signed values.
+
+define i8 @to_f8e5m3fnu_dynamic(float %x) {
+; CHECK-LABEL: to_f8e5m3fnu_dynamic:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    movd %xmm0, (%rsp) # 4-byte Folded Spill
+; CHECK-NEXT:    leaq {{[0-9]+}}(%rsp), %rdi
+; CHECK-NEXT:    callq frexpf at PLT
+; CHECK-NEXT:    movl {{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl $7, %ecx
+; CHECK-NEXT:    subl %edx, %ecx
+; CHECK-NEXT:    cmpl $31, %ecx
+; CHECK-NEXT:    movl $31, %eax
+; CHECK-NEXT:    cmovbl %ecx, %eax
+; CHECK-NEXT:    cmpl $1, %eax
+; CHECK-NEXT:    movl %eax, %ecx
+; CHECK-NEXT:    adcl $-1, %ecx
+; CHECK-NEXT:    movd %xmm0, %esi
+; CHECK-NEXT:    movl %esi, %edi
+; CHECK-NEXT:    andl $8388607, %edi # imm = 0x7FFFFF
+; CHECK-NEXT:    leal 8388608(%rdi), %r8d
+; CHECK-NEXT:    movl %r8d, %r9d
+; CHECK-NEXT:    shrl %cl, %r9d
+; CHECK-NEXT:    movl $1, %r10d
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shll %cl, %r10d
+; CHECK-NEXT:    decl %r10d
+; CHECK-NEXT:    xorl %r11d, %r11d
+; CHECK-NEXT:    testl %r10d, %r8d
+; CHECK-NEXT:    setne %r11b
+; CHECK-NEXT:    movl %eax, %ecx
+; CHECK-NEXT:    shrl %cl, %r8d
+; CHECK-NEXT:    movl %r8d, %r10d
+; CHECK-NEXT:    andl $1, %r10d
+; CHECK-NEXT:    orl %r11d, %r10d
+; CHECK-NEXT:    andl %r9d, %r10d
+; CHECK-NEXT:    xorl %ecx, %ecx
+; CHECK-NEXT:    cmpl $1, %eax
+; CHECK-NEXT:    cmovbl %ecx, %r10d
+; CHECK-NEXT:    addl %r8d, %r10d
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    cmpl $8, %r10d
+; CHECK-NEXT:    cmovgel %ecx, %r10d
+; CHECK-NEXT:    setge %al
+; CHECK-NEXT:    shll $3, %eax
+; CHECK-NEXT:    orl %r10d, %eax
+; CHECK-NEXT:    shrl $20, %edi
+; CHECK-NEXT:    movl %edi, %r8d
+; CHECK-NEXT:    andl $1, %r8d
+; CHECK-NEXT:    xorl %r9d, %r9d
+; CHECK-NEXT:    testl $524287, %esi # imm = 0x7FFFF
+; CHECK-NEXT:    setne %r9b
+; CHECK-NEXT:    orl %r8d, %r9d
+; CHECK-NEXT:    shrl $19, %esi
+; CHECK-NEXT:    andl %r9d, %esi
+; CHECK-NEXT:    addl %edi, %esi
+; CHECK-NEXT:    xorl %edi, %edi
+; CHECK-NEXT:    cmpl $8, %esi
+; CHECK-NEXT:    cmovgel %ecx, %esi
+; CHECK-NEXT:    setge %dil
+; CHECK-NEXT:    leal (%rdx,%rdi), %r8d
+; CHECK-NEXT:    leal 112(,%r8,8), %r8d
+; CHECK-NEXT:    orl %esi, %r8d
+; CHECK-NEXT:    leal 14(%rdx,%rdi), %edx
+; CHECK-NEXT:    testl %edx, %edx
+; CHECK-NEXT:    cmovlel %eax, %r8d
+; CHECK-NEXT:    pxor %xmm0, %xmm0
+; CHECK-NEXT:    movss (%rsp), %xmm1 # 4-byte Reload
+; CHECK-NEXT:    # xmm1 = mem[0],zero,zero,zero
+; CHECK-NEXT:    ucomiss %xmm0, %xmm1
+; CHECK-NEXT:    cmovnel %r8d, %ecx
+; CHECK-NEXT:    cmovpl %r8d, %ecx
+; CHECK-NEXT:    movl $255, %eax
+; CHECK-NEXT:    cmovnpl %ecx, %eax
+; CHECK-NEXT:    # kill: def $al killed $al killed $eax
+; CHECK-NEXT:    popq %rcx
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(
+      float %x, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+  ret i8 %r
+}
+
+; Saturating: negatives clamp to zero, overflow clamps to max finite.
+define i8 @to_f8e5m3fnu_saturate(float %x) {
+; CHECK-LABEL: to_f8e5m3fnu_saturate:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    subq $40, %rsp
+; CHECK-NEXT:    .cfi_def_cfa_offset 48
+; CHECK-NEXT:    movdqa %xmm0, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; CHECK-NEXT:    leaq {{[0-9]+}}(%rsp), %rdi
+; CHECK-NEXT:    callq frexpf at PLT
+; CHECK-NEXT:    movl {{[0-9]+}}(%rsp), %edx
+; CHECK-NEXT:    movl $7, %ecx
+; CHECK-NEXT:    subl %edx, %ecx
+; CHECK-NEXT:    cmpl $31, %ecx
+; CHECK-NEXT:    movl $31, %eax
+; CHECK-NEXT:    cmovbl %ecx, %eax
+; CHECK-NEXT:    cmpl $1, %eax
+; CHECK-NEXT:    movl %eax, %ecx
+; CHECK-NEXT:    adcl $-1, %ecx
+; CHECK-NEXT:    movd %xmm0, %esi
+; CHECK-NEXT:    movl %esi, %edi
+; CHECK-NEXT:    andl $8388607, %edi # imm = 0x7FFFFF
+; CHECK-NEXT:    leal 8388608(%rdi), %r8d
+; CHECK-NEXT:    movl %r8d, %r9d
+; CHECK-NEXT:    shrl %cl, %r9d
+; CHECK-NEXT:    movl $1, %r10d
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shll %cl, %r10d
+; CHECK-NEXT:    decl %r10d
+; CHECK-NEXT:    xorl %r11d, %r11d
+; CHECK-NEXT:    testl %r10d, %r8d
+; CHECK-NEXT:    setne %r11b
+; CHECK-NEXT:    movl %eax, %ecx
+; CHECK-NEXT:    shrl %cl, %r8d
+; CHECK-NEXT:    movl %r8d, %r10d
+; CHECK-NEXT:    andl $1, %r10d
+; CHECK-NEXT:    orl %r11d, %r10d
+; CHECK-NEXT:    andl %r9d, %r10d
+; CHECK-NEXT:    xorl %ecx, %ecx
+; CHECK-NEXT:    cmpl $1, %eax
+; CHECK-NEXT:    cmovbl %ecx, %r10d
+; CHECK-NEXT:    addl %r8d, %r10d
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    cmpl $8, %r10d
+; CHECK-NEXT:    cmovgel %ecx, %r10d
+; CHECK-NEXT:    setge %al
+; CHECK-NEXT:    shll $3, %eax
+; CHECK-NEXT:    orl %r10d, %eax
+; CHECK-NEXT:    shrl $20, %edi
+; CHECK-NEXT:    movl %edi, %r8d
+; CHECK-NEXT:    andl $1, %r8d
+; CHECK-NEXT:    xorl %r9d, %r9d
+; CHECK-NEXT:    testl $524287, %esi # imm = 0x7FFFF
+; CHECK-NEXT:    setne %r9b
+; CHECK-NEXT:    orl %r8d, %r9d
+; CHECK-NEXT:    shrl $19, %esi
+; CHECK-NEXT:    andl %r9d, %esi
+; CHECK-NEXT:    addl %edi, %esi
+; CHECK-NEXT:    xorl %edi, %edi
+; CHECK-NEXT:    cmpl $8, %esi
+; CHECK-NEXT:    cmovgel %ecx, %esi
+; CHECK-NEXT:    setge %dil
+; CHECK-NEXT:    leal (%rdx,%rdi), %r8d
+; CHECK-NEXT:    leal 112(,%r8,8), %r8d
+; CHECK-NEXT:    orl %esi, %r8d
+; CHECK-NEXT:    leal 14(%rdx,%rdi), %edx
+; CHECK-NEXT:    testl %edx, %edx
+; CHECK-NEXT:    cmovlel %eax, %r8d
+; CHECK-NEXT:    cmpl $7, %esi
+; CHECK-NEXT:    setge %al
+; CHECK-NEXT:    cmpl $31, %edx
+; CHECK-NEXT:    sete %sil
+; CHECK-NEXT:    andb %al, %sil
+; CHECK-NEXT:    cmpl $32, %edx
+; CHECK-NEXT:    setge %al
+; CHECK-NEXT:    orb %sil, %al
+; CHECK-NEXT:    movl $254, %edx
+; CHECK-NEXT:    cmovnel %edx, %r8d
+; CHECK-NEXT:    pxor %xmm0, %xmm0
+; CHECK-NEXT:    movaps {{[-0-9]+}}(%r{{[sb]}}p), %xmm2 # 16-byte Reload
+; CHECK-NEXT:    ucomiss %xmm0, %xmm2
+; CHECK-NEXT:    movl $0, %eax
+; CHECK-NEXT:    cmovnel %r8d, %eax
+; CHECK-NEXT:    cmovpl %r8d, %eax
+; CHECK-NEXT:    movaps {{.*#+}} xmm1 = [NaN,NaN,NaN,NaN]
+; CHECK-NEXT:    andps %xmm2, %xmm1
+; CHECK-NEXT:    ucomiss {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm1
+; CHECK-NEXT:    cmovnel %eax, %edx
+; CHECK-NEXT:    cmovpl %eax, %edx
+; CHECK-NEXT:    ucomiss %xmm2, %xmm0
+; CHECK-NEXT:    cmoval %ecx, %edx
+; CHECK-NEXT:    ucomiss %xmm0, %xmm2
+; CHECK-NEXT:    movl $255, %eax
+; CHECK-NEXT:    cmovnpl %edx, %eax
+; CHECK-NEXT:    # kill: def $al killed $al killed $eax
+; CHECK-NEXT:    addq $40, %rsp
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; CHECK-NEXT:    retq
+  %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(
+      float %x, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 true)
+  ret i8 %r
+}
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 0c5103c4b49e4..4e0deddb9ba8a 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -10783,6 +10783,8 @@ TEST(APFloatTest, getArbitraryFPSemantics) {
             APFloat::getArbitraryFPSemantics("Float8E5M2"));
   EXPECT_EQ(&APFloat::Float8E4M3FN(),
             APFloat::getArbitraryFPSemantics("Float8E4M3FN"));
+  EXPECT_EQ(&APFloat::Float8E5M3FNU(),
+            APFloat::getArbitraryFPSemantics("Float8E5M3FNU"));
   EXPECT_EQ(&APFloat::Float6E3M2FN(),
             APFloat::getArbitraryFPSemantics("Float6E3M2FN"));
   EXPECT_EQ(&APFloat::Float6E2M3FN(),
@@ -10797,7 +10799,6 @@ TEST(APFloatTest, getArbitraryFPSemantics) {
   EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E4M3B11FNUZ"));
   EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E3M4"));
   EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E8M0FNU"));
-  EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics("Float8E5M3FNU"));
 
   // Invalid formats report no semantics.
   EXPECT_EQ(nullptr, APFloat::getArbitraryFPSemantics(""));



More information about the llvm-commits mailing list