diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 1f974c148af4..44a1240f5635 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5727,6 +5727,45 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, Known.knownNot(fcNan | fcInf | fcNegative); break; } + case Intrinsic::convert_from_arbitrary_fp: { + auto *MD = cast(II->getArgOperand(1))->getMetadata(); + StringRef FormatStr = cast(MD)->getString(); + + const fltSemantics *SrcSemantics = + APFloat::getArbitraryFPSemantics(FormatStr); + if (!SrcSemantics) + break; + + const fltSemantics DstSemantics = + II->getType()->getScalarType()->getFltSemantics(); + + if (!APFloat::semanticsHasNaN(*SrcSemantics)) + Known.knownNot(fcNan); + + // fcInf can only be cleared if the source format has no Inf encoding + // and the dst max exp can accommodate src max exp. + if (!APFloat::semanticsHasInf(*SrcSemantics) && + APFloat::semanticsMaxExponent(*SrcSemantics) <= + APFloat::semanticsMaxExponent(DstSemantics)) + Known.knownNot(fcInf); + + // Check and clear all neg flags for formats that do not have signed + // representation. + if (!APFloat::semanticsHasSignedRepr(*SrcSemantics)) + Known.knownNot(fcNegative); + + // Check if format has no zero at all (Float8E8M0FNU), or no negative + // zero. + if (!APFloat::semanticsHasZero(*SrcSemantics)) + Known.knownNot(fcZero); + else if (SrcSemantics->nanEncoding == fltNanEncoding::NegativeZero) + Known.knownNot(fcNegZero); + + // If src lands normally in dest, the result can never be subnormal. + if (APFloat::isRepresentableAsNormalIn(*SrcSemantics, DstSemantics)) + Known.knownNot(fcSubnormal); + break; + } default: break; } diff --git a/llvm/test/Transforms/Attributor/nofpclass-arbitrary-fp.ll b/llvm/test/Transforms/Attributor/nofpclass-arbitrary-fp.ll new file mode 100644 index 000000000000..c87a983882f6 --- /dev/null +++ b/llvm/test/Transforms/Attributor/nofpclass-arbitrary-fp.ll @@ -0,0 +1,192 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s + +define float @test_convert_float8e5m2_to_f32(i8 %bits) { +; CHECK-LABEL: define nofpclass(sub) float @test_convert_float8e5m2_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(sub) float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E5M2") #[[ATTR2:[0-9]+]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E5M2") + ret float %fp +} + +define float @test_convert_float8e5m2fnuz_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e5m2fnuz_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E5M2FNUZ") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E5M2FNUZ") + ret float %fp +} + +define float @test_convert_float8e4m3_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e4m3_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E4M3") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E4M3") + ret float %fp +} + +define float @test_convert_float8e4m3fn_to_f32(i8 %bits) { +; CHECK-LABEL: define nofpclass(inf sub) float @test_convert_float8e4m3fn_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(inf sub) float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E4M3FN") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E4M3FN") + ret float %fp +} + +define float @test_convert_float8e4m3fnuz_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e4m3fnuz_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E4M3FNUZ") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E4M3FNUZ") + ret float %fp +} + +define float @test_convert_float8e4m3b11fnuz_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e4m3b11fnuz_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E4M3B11FNUZ") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E4M3B11FNUZ") + ret float %fp +} + +define float @test_convert_float8e3m4_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e3m4_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E3M4") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E3M4") + ret float %fp +} + +define float @test_convert_float8e8m0fnu_to_f32(i8 %bits) { +; CHECK-LABEL: define float @test_convert_float8e8m0fnu_to_f32( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 [[BITS]], metadata !"Float8E8M0FNU") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i8(i8 %bits, metadata !"Float8E8M0FNU") + ret float %fp +} + +define float @test_convert_float6e3m2fn_to_f32(i6 %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) float @test_convert_float6e3m2fn_to_f32( +; CHECK-SAME: i6 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) float @llvm.convert.from.arbitrary.fp.f32.i6(i6 [[BITS]], metadata !"Float6E3M2FN") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i6(i6 %bits, metadata !"Float6E3M2FN") + ret float %fp +} + +define float @test_convert_float6e2m3fn_to_f32(i6 %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) float @test_convert_float6e2m3fn_to_f32( +; CHECK-SAME: i6 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) float @llvm.convert.from.arbitrary.fp.f32.i6(i6 [[BITS]], metadata !"Float6E2M3FN") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i6(i6 %bits, metadata !"Float6E2M3FN") + ret float %fp +} + +define float @test_convert_float4e2m1fn_to_f32(i4 %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) float @test_convert_float4e2m1fn_to_f32( +; CHECK-SAME: i4 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) float @llvm.convert.from.arbitrary.fp.f32.i4(i4 [[BITS]], metadata !"Float4E2M1FN") #[[ATTR2]] +; CHECK-NEXT: ret float [[FP]] +; + %fp = call float @llvm.convert.from.arbitrary.fp.f32.i4(i4 %bits, metadata !"Float4E2M1FN") + ret float %fp +} + +define double @test_convert_float4e2m1fn_to_f64(i4 %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) double @test_convert_float4e2m1fn_to_f64( +; CHECK-SAME: i4 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) double @llvm.convert.from.arbitrary.fp.f64.i4(i4 [[BITS]], metadata !"Float4E2M1FN") #[[ATTR2]] +; CHECK-NEXT: ret double [[FP]] +; + %fp = call double @llvm.convert.from.arbitrary.fp.f64.i4(i4 %bits, metadata !"Float4E2M1FN") + ret double %fp +} + +define half @test_convert_float8e4m3fn_to_f16(i8 %bits) { +; CHECK-LABEL: define nofpclass(inf sub) half @test_convert_float8e4m3fn_to_f16( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(inf sub) half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[BITS]], metadata !"Float8E4M3FN") #[[ATTR2]] +; CHECK-NEXT: ret half [[FP]] +; + %fp = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %bits, metadata !"Float8E4M3FN") + ret half %fp +} + +define half @test_convert_float6e2m3fn_to_f16(i6 %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) half @test_convert_float6e2m3fn_to_f16( +; CHECK-SAME: i6 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) half @llvm.convert.from.arbitrary.fp.f16.i6(i6 [[BITS]], metadata !"Float6E2M3FN") #[[ATTR2]] +; CHECK-NEXT: ret half [[FP]] +; + %fp = call half @llvm.convert.from.arbitrary.fp.f16.i6(i6 %bits, metadata !"Float6E2M3FN") + ret half %fp +} + +define <4 x half> @test_convert_float8e5m2_to_v4f16(<4 x i8> %bits) { +; CHECK-LABEL: define <4 x half> @test_convert_float8e5m2_to_v4f16( +; CHECK-SAME: <4 x i8> [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call <4 x half> @llvm.convert.from.arbitrary.fp.v4f16.v4i8(<4 x i8> [[BITS]], metadata !"Float8E5M2") #[[ATTR2]] +; CHECK-NEXT: ret <4 x half> [[FP]] +; + %fp = call <4 x half> @llvm.convert.from.arbitrary.fp.v4f16.v4i8(<4 x i8> %bits, metadata !"Float8E5M2") + ret <4 x half> %fp +} + +define <4 x double> @test_convert_float4e2m1fn_to_v4f64(<4 x i4> %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) <4 x double> @test_convert_float4e2m1fn_to_v4f64( +; CHECK-SAME: <4 x i4> [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i4(<4 x i4> [[BITS]], metadata !"Float4E2M1FN") #[[ATTR2]] +; CHECK-NEXT: ret <4 x double> [[FP]] +; + %fp = call <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i4(<4 x i4> %bits, metadata !"Float4E2M1FN") + ret <4 x double> %fp +} + +define <4 x double> @test_convert_float6e3m2fn_to_v4f64(<4 x i6> %bits) { +; CHECK-LABEL: define nofpclass(nan inf sub) <4 x double> @test_convert_float6e3m2fn_to_v4f64( +; CHECK-SAME: <4 x i6> [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call nofpclass(nan inf sub) <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i6(<4 x i6> [[BITS]], metadata !"Float6E3M2FN") #[[ATTR2]] +; CHECK-NEXT: ret <4 x double> [[FP]] +; + %fp = call <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i6(<4 x i6> %bits, metadata !"Float6E3M2FN") + ret <4 x double> %fp +} + +define half @test_convert_float8e8m0fnu_to_f16(i8 %bits) { +; CHECK-LABEL: define half @test_convert_float8e8m0fnu_to_f16( +; CHECK-SAME: i8 [[BITS:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FP:%.*]] = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 [[BITS]], metadata !"Float8E8M0FNU") #[[ATTR2]] +; CHECK-NEXT: ret half [[FP]] +; + %fp = call half @llvm.convert.from.arbitrary.fp.f16.i8(i8 %bits, metadata !"Float8E8M0FNU") + ret half %fp +} + +declare float @llvm.convert.from.arbitrary.fp.f32.i4(i4, metadata) +declare float @llvm.convert.from.arbitrary.fp.f32.i6(i6, metadata) +declare float @llvm.convert.from.arbitrary.fp.f32.i8(i8, metadata) +declare double @llvm.convert.from.arbitrary.fp.f64.i4(i4, metadata) +declare half @llvm.convert.from.arbitrary.fp.f16.i8(i8, metadata) +declare half @llvm.convert.from.arbitrary.fp.f16.i6(i6, metadata) +declare <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i4(<4 x i4>, metadata) +declare <4 x half> @llvm.convert.from.arbitrary.fp.v4f16.v4i8(<4 x i8>, metadata) +declare <4 x double> @llvm.convert.from.arbitrary.fp.v4f64.v4i6(<4 x i6>, metadata)