[llvm] [InstSimplify] Fold the arbitrary FP convert round trip (PR #223052)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 13:10:41 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/223052
>From 8581e96b9b005cc36d49a16393f545583fc3b767 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 11 Sep 2026 22:37:42 +0200
Subject: [PATCH 1/2] [InstSimplify] Fold the arbitrary FP convert round trip
Fold to X when every Fmt encoding survives the native type
---
llvm/lib/Analysis/InstructionSimplify.cpp | 58 +++++
.../InstSimplify/arbitrary-fp-convert.ll | 230 ++++++++++++++++++
2 files changed, 288 insertions(+)
create mode 100644 llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d9aebc757d636..3627f2432d6d2 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7346,6 +7346,62 @@ static Value *simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
return nullptr;
}
+/// convert.to.arbitrary.fp(convert.from.arbitrary.fp(X, Fmt), Fmt, RM, Sat)
+/// --> X. RM is unused: every value the round trip reaches is exact in Fmt.
+static Value *simplifyArbitraryFPRoundTrip(Type *ReturnType,
+ ArrayRef<Value *> Args,
+ const SimplifyQuery &Q,
+ Function *CxtF) {
+ if (Args.size() != 4)
+ return nullptr;
+
+ auto *From = dyn_cast<IntrinsicInst>(Args[0]);
+ if (!From || From->getIntrinsicID() != Intrinsic::convert_from_arbitrary_fp)
+ return nullptr;
+
+ Value *X = From->getArgOperand(0);
+ if (X->getType() != ReturnType)
+ return nullptr;
+
+ // MDString is uniqued, so equal formats are the same operand.
+ if (Args[1] != From->getArgOperand(1))
+ return nullptr;
+
+ auto *Format =
+ dyn_cast<MDString>(cast<MetadataAsValue>(Args[1])->getMetadata());
+ if (!Format)
+ return nullptr;
+
+ const fltSemantics *Src =
+ APFloat::getArbitraryFPSemantics(Format->getString());
+ if (!Src)
+ return nullptr;
+
+ const fltSemantics &Dst = From->getType()->getScalarType()->getFltSemantics();
+ if (!APFloat::isLosslesslyConvertibleTo(*Src, Dst, /*IgnoreNaNs=*/true))
+ return nullptr;
+
+ // Inf is then the only out-of-range input, and saturation maps it to finite.
+ if (APFloat::semanticsHasInf(*Src) && !match(Args[3], m_Zero()))
+ return nullptr;
+
+ if (!APFloat::isRepresentableAsNormalIn(*Src, Dst)) {
+ // From may be detached, but its function, not CxtF, decides the mode.
+ const Function *F = From->getParent() ? From->getFunction() : CxtF;
+ if (!F)
+ return nullptr;
+ DenormalMode Mode = F->getDenormalMode(Dst);
+ if (Mode.inputsMayBeZero() || Mode.outputsMayBeZero())
+ return nullptr;
+ }
+
+ // A NaN result has a non-deterministic sign and payload.
+ if (APFloat::semanticsHasNaN(*Src) && !isKnownNeverNaN(From, Q))
+ return nullptr;
+
+ return X;
+}
+
/// interleaveN(extractvalue(deinterleaveN(x), 0), ...,
/// extractvalue(deinterleaveN(x), N-1)) --> x
static Value *simplifyIdentityInterleave(Intrinsic::ID IID,
@@ -7419,6 +7475,8 @@ Value *llvm::simplifyIntrinsic(Intrinsic::ID IID, Type *ReturnType,
// Handle intrinsics with 3 or more arguments.
switch (IID) {
+ case Intrinsic::convert_to_arbitrary_fp:
+ return simplifyArbitraryFPRoundTrip(ReturnType, Args, Q, CxtF);
case Intrinsic::masked_load:
case Intrinsic::masked_gather: {
Value *MaskArg = Args[1];
diff --git a/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll b/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
new file mode 100644
index 0000000000000..2fa83d7699b19
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
@@ -0,0 +1,230 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=instsimplify -S < %s | FileCheck %s
+
+define i8 @round_trip_e4m3fn_f32(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e4m3fn_f32(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E4M3FN")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e4m3fn_f16(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e4m3fn_f16(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E4M3FN")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E4M3FN", metadata !"round.towardzero", i1 true)
+ ret i8 %r
+}
+
+define <4 x i4> @round_trip_e2m1fn_v4f32(<4 x i4> %x) {
+; CHECK-LABEL: define <4 x i4> @round_trip_e2m1fn_v4f32(
+; CHECK-SAME: <4 x i4> [[X:%.*]]) {
+; CHECK-NEXT: ret <4 x i4> [[X]]
+;
+ %f = call <4 x float> @llvm.convert.from.arbitrary.fp.v4f32.v4i4(<4 x i4> %x, metadata !"Float4E2M1FN")
+ %r = call <4 x i4> @llvm.convert.to.arbitrary.fp.v4i4.v4f32(<4 x float> %f, metadata !"Float4E2M1FN", metadata !"round.tonearest", i1 true)
+ ret <4 x i4> %r
+}
+
+define i6 @round_trip_e3m2fn_f16(i6 %x) {
+; CHECK-LABEL: define i6 @round_trip_e3m2fn_f16(
+; CHECK-SAME: i6 [[X:%.*]]) {
+; CHECK-NEXT: ret i6 [[X]]
+;
+ %f = call half @llvm.convert.from.arbitrary.fp.f16.i6(i6 %x, metadata !"Float6E3M2FN")
+ %r = call i6 @llvm.convert.to.arbitrary.fp.i6.f16(half %f, metadata !"Float6E3M2FN", metadata !"round.tonearest", i1 true)
+ ret i6 %r
+}
+
+; E5M3FNU has a wider exponent range than half, so large values round to Inf.
+define i8 @round_trip_e5m3fnu_f16(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m3fnu_f16(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M3FNU")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E5M3FNU")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; E5M2 NaN payloads may change in the inner conversion.
+define i8 @round_trip_e5m2_f32(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f32(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_f32_saturating(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f32_saturating(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 true)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 true)
+ ret i8 %r
+}
+
+; E5M2 subnormals are subnormal in half too.
+define i8 @round_trip_e5m2_f16_daz(i8 %x) #0 {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f16_daz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; Flushing on output alone is enough to lose the subnormal encodings.
+define i8 @round_trip_e5m2_f16_ftz(i8 %x) #1 {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f16_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; A dynamic mode may turn out to flush.
+define i8 @round_trip_e5m2_f16_dynamic(i8 %x) #2 {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f16_dynamic(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; E4M3FN lands on half normals, so flushing cannot reach it.
+define i8 @round_trip_e4m3fn_f16_ftz(i8 %x) #3 {
+; CHECK-LABEL: define i8 @round_trip_e4m3fn_f16_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E4M3FN")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_f16_ieee(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f16_ieee(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; E4M3FN NaN encodings 0x7F and 0xFF interchange under a non-deterministic sign.
+define i8 @round_trip_e4m3fn_f32_maynan(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e4m3fn_f32_maynan(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E4M3FN")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E4M3FN")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m3fnu_f32(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m3fnu_f32(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E5M3FNU")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E5M3FNU", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @not_a_round_trip(float %f) {
+; CHECK-LABEL: define i8 @not_a_round_trip(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_diff_format(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_diff_format(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_multi_use(i8 %x, ptr %p) {
+; CHECK-LABEL: define i8 @round_trip_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E4M3FN")
+; CHECK-NEXT: store float [[F]], ptr [[P]], align 4
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E4M3FN")
+ store float %f, ptr %p
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+; Float8E3M4 is valid but unmapped. Delete when getArbitraryFPSemantics covers it.
+define i8 @round_trip_unsupported_format(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_unsupported_format(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E3M4")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E3M4", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E3M4")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E3M4", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define <4 x i8> @round_trip_e5m2_v4bf16_nnan(<4 x i8> %x, ptr %p) {
+; CHECK-LABEL: define <4 x i8> @round_trip_e5m2_v4bf16_nnan(
+; CHECK-SAME: <4 x i8> [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan <4 x bfloat> @llvm.convert.from.arbitrary.fp.v4bf16.v4i8(<4 x i8> [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: store <4 x bfloat> [[F]], ptr [[P]], align 8
+; CHECK-NEXT: ret <4 x i8> [[X]]
+;
+ %f = call nnan <4 x bfloat> @llvm.convert.from.arbitrary.fp.v4bf16.v4i8(<4 x i8> %x, metadata !"Float8E5M2")
+ store <4 x bfloat> %f, ptr %p
+ %r = call <4 x i8> @llvm.convert.to.arbitrary.fp.v4i8.v4bf16(<4 x bfloat> %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret <4 x i8> %r
+}
+
+; denormal_fpenv is written output|input.
+attributes #0 = { denormal_fpenv(ieee|preservesign) }
+attributes #1 = { denormal_fpenv(preservesign|ieee) }
+attributes #2 = { denormal_fpenv(dynamic) }
+attributes #3 = { denormal_fpenv(preservesign) }
>From 68f68e4597b0807b73a62fa29092b2a3f2d65065 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Sat, 12 Sep 2026 21:58:24 +0200
Subject: [PATCH 2/2] add tests
---
.../InstSimplify/arbitrary-fp-convert.ll | 92 ++++++++++++++++++-
1 file changed, 90 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll b/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
index 2fa83d7699b19..1011baf67a2a0 100644
--- a/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
+++ b/llvm/test/Transforms/InstSimplify/arbitrary-fp-convert.ll
@@ -11,6 +11,58 @@ define i8 @round_trip_e4m3fn_f32(i8 %x) {
ret i8 %r
}
+define i8 @round_trip_e4m3fn_f64(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e4m3fn_f64(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan double @llvm.convert.from.arbitrary.fp.f64.i8(i8 %x, metadata !"Float8E4M3FN")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double %f, metadata !"Float8E4M3FN", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_f64(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f64(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan double @llvm.convert.from.arbitrary.fp.f64.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_f64_ftz(i8 %x) #1 {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f64_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan double @llvm.convert.from.arbitrary.fp.f64.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_f80(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_f80(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i8 [[X]]
+;
+ %f = call nnan x86_fp80 @llvm.convert.from.arbitrary.fp.f80.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f80(x86_fp80 %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e5m2_ppcf128(i8 %x) {
+; CHECK-LABEL: define i8 @round_trip_e5m2_ppcf128(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[F:%.*]] = call nnan ppc_fp128 @llvm.convert.from.arbitrary.fp.ppcf128.i8(i8 [[X]], metadata !"Float8E5M2")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.ppcf128(ppc_fp128 [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan ppc_fp128 @llvm.convert.from.arbitrary.fp.ppcf128.i8(i8 %x, metadata !"Float8E5M2")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.ppcf128(ppc_fp128 %f, metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
define i8 @round_trip_e4m3fn_f16(i8 %x) {
; CHECK-LABEL: define i8 @round_trip_e4m3fn_f16(
; CHECK-SAME: i8 [[X:%.*]]) {
@@ -82,7 +134,7 @@ define i8 @round_trip_e5m2_f32_saturating(i8 %x) {
; E5M2 subnormals are subnormal in half too.
define i8 @round_trip_e5m2_f16_daz(i8 %x) #0 {
; CHECK-LABEL: define i8 @round_trip_e5m2_f16_daz(
-; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M2")
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
; CHECK-NEXT: ret i8 [[R]]
@@ -95,7 +147,7 @@ define i8 @round_trip_e5m2_f16_daz(i8 %x) #0 {
; Flushing on output alone is enough to lose the subnormal encodings.
define i8 @round_trip_e5m2_f16_ftz(i8 %x) #1 {
; CHECK-LABEL: define i8 @round_trip_e5m2_f16_ftz(
-; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[F:%.*]] = call nnan half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[X]], metadata !"Float8E5M2")
; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f16(half [[F]], metadata !"Float8E5M2", metadata !"round.tonearest", i1 false)
; CHECK-NEXT: ret i8 [[R]]
@@ -210,6 +262,42 @@ define i8 @round_trip_unsupported_format(i8 %x) {
ret i8 %r
}
+define i8 @round_trip_e8m0fnu_f32_ftz(i8 %x) #1 {
+; CHECK-LABEL: define i8 @round_trip_e8m0fnu_f32_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[X]], metadata !"Float8E8M0FNU")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float [[F]], metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %x, metadata !"Float8E8M0FNU")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f32(float %f, metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e8m0fnu_bf16_ftz(i8 %x) #1 {
+; CHECK-LABEL: define i8 @round_trip_e8m0fnu_bf16_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan bfloat @llvm.convert.from.arbitrary.fp.bf16.i8(i8 [[X]], metadata !"Float8E8M0FNU")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.bf16(bfloat [[F]], metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan bfloat @llvm.convert.from.arbitrary.fp.bf16.i8(i8 %x, metadata !"Float8E8M0FNU")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.bf16(bfloat %f, metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
+define i8 @round_trip_e8m0fnu_f64_ftz(i8 %x) #1 {
+; CHECK-LABEL: define i8 @round_trip_e8m0fnu_f64_ftz(
+; CHECK-SAME: i8 [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[F:%.*]] = call nnan double @llvm.convert.from.arbitrary.fp.f64.i8(i8 [[X]], metadata !"Float8E8M0FNU")
+; CHECK-NEXT: [[R:%.*]] = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double [[F]], metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %f = call nnan double @llvm.convert.from.arbitrary.fp.f64.i8(i8 %x, metadata !"Float8E8M0FNU")
+ %r = call i8 @llvm.convert.to.arbitrary.fp.i8.f64(double %f, metadata !"Float8E8M0FNU", metadata !"round.tonearest", i1 false)
+ ret i8 %r
+}
+
define <4 x i8> @round_trip_e5m2_v4bf16_nnan(<4 x i8> %x, ptr %p) {
; CHECK-LABEL: define <4 x i8> @round_trip_e5m2_v4bf16_nnan(
; CHECK-SAME: <4 x i8> [[X:%.*]], ptr [[P:%.*]]) {
More information about the llvm-commits
mailing list