[Analysis] Adding convert_from_arbitrary_fp support in computeKnownFPClass. (#208585)
fixes #208437 From LangRef for intrinsic convert_from_arbitrary_fp , "Normal finite values are converted exactly. NaN values follow LLVM’s standard NaN rules; the NaN representation is preserved... and the NaN payload may be truncated or extended..." -> if src has Nan -> preserve nan "If a value exceeds the representable range of the target type (for example, converting Float8E8M0FNU with large exponents to half), the result is converted to infinity with the appropriate sign."
This commit is contained in:
parent
03de9b148a
commit
dc5f2aae75
2 changed files with 231 additions and 0 deletions
|
|
@ -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<MetadataAsValue>(II->getArgOperand(1))->getMetadata();
|
||||
StringRef FormatStr = cast<MDString>(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;
|
||||
}
|
||||
|
|
|
|||
192
llvm/test/Transforms/Attributor/nofpclass-arbitrary-fp.ll
Normal file
192
llvm/test/Transforms/Attributor/nofpclass-arbitrary-fp.ll
Normal file
|
|
@ -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)
|
||||
Loading…
Reference in a new issue