Skip to content

Commit 1dbade8

Browse files
authored
[DirectX] Add emulation for fp16 types of llvm.is.fpclass (#157505)
fixes #157504 This changes adds the emulation we need for IsNaN, IsNormal, & IsFinite This change only applies these emulations to the llvm.is.fpclass cases of fp16. Since there is no DX intrinsics yet for these cases, applying the emulation to the necessary intrinsics is left for future implementers of - #99132 - #156069 - #99131
1 parent 393bc0c commit 1dbade8

File tree

2 files changed

+261
-2
lines changed

2 files changed

+261
-2
lines changed

llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,115 @@ static Value *expand16BitIsInf(CallInst *Orig) {
8686
return B3;
8787
}
8888

89+
static Value *expand16BitIsNaN(CallInst *Orig) {
90+
Module *M = Orig->getModule();
91+
if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
92+
return nullptr;
93+
94+
Value *Val = Orig->getOperand(0);
95+
Type *ValTy = Val->getType();
96+
if (!ValTy->getScalarType()->isHalfTy())
97+
return nullptr;
98+
99+
IRBuilder<> Builder(Orig);
100+
Type *IType = Type::getInt16Ty(M->getContext());
101+
102+
Constant *ExpBitMask =
103+
ValTy->isVectorTy()
104+
? ConstantVector::getSplat(
105+
ElementCount::getFixed(
106+
cast<FixedVectorType>(ValTy)->getNumElements()),
107+
ConstantInt::get(IType, 0x7c00))
108+
: ConstantInt::get(IType, 0x7c00);
109+
Constant *SigBitMask =
110+
ValTy->isVectorTy()
111+
? ConstantVector::getSplat(
112+
ElementCount::getFixed(
113+
cast<FixedVectorType>(ValTy)->getNumElements()),
114+
ConstantInt::get(IType, 0x3ff))
115+
: ConstantInt::get(IType, 0x3ff);
116+
117+
Constant *Zero =
118+
ValTy->isVectorTy()
119+
? ConstantVector::getSplat(
120+
ElementCount::getFixed(
121+
cast<FixedVectorType>(ValTy)->getNumElements()),
122+
ConstantInt::get(IType, 0))
123+
: ConstantInt::get(IType, 0);
124+
125+
Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
126+
Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
127+
Value *B1 = Builder.CreateICmpEQ(Exp, ExpBitMask);
128+
129+
Value *Sig = Builder.CreateAnd(IVal, SigBitMask);
130+
Value *B2 = Builder.CreateICmpNE(Sig, Zero);
131+
Value *B3 = Builder.CreateAnd(B1, B2);
132+
return B3;
133+
}
134+
135+
static Value *expand16BitIsFinite(CallInst *Orig) {
136+
Module *M = Orig->getModule();
137+
if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
138+
return nullptr;
139+
140+
Value *Val = Orig->getOperand(0);
141+
Type *ValTy = Val->getType();
142+
if (!ValTy->getScalarType()->isHalfTy())
143+
return nullptr;
144+
145+
IRBuilder<> Builder(Orig);
146+
Type *IType = Type::getInt16Ty(M->getContext());
147+
148+
Constant *ExpBitMask =
149+
ValTy->isVectorTy()
150+
? ConstantVector::getSplat(
151+
ElementCount::getFixed(
152+
cast<FixedVectorType>(ValTy)->getNumElements()),
153+
ConstantInt::get(IType, 0x7c00))
154+
: ConstantInt::get(IType, 0x7c00);
155+
156+
Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
157+
Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
158+
Value *B1 = Builder.CreateICmpNE(Exp, ExpBitMask);
159+
return B1;
160+
}
161+
162+
static Value *expand16BitIsNormal(CallInst *Orig) {
163+
Module *M = Orig->getModule();
164+
if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
165+
return nullptr;
166+
167+
Value *Val = Orig->getOperand(0);
168+
Type *ValTy = Val->getType();
169+
if (!ValTy->getScalarType()->isHalfTy())
170+
return nullptr;
171+
172+
IRBuilder<> Builder(Orig);
173+
Type *IType = Type::getInt16Ty(M->getContext());
174+
175+
Constant *ExpBitMask =
176+
ValTy->isVectorTy()
177+
? ConstantVector::getSplat(
178+
ElementCount::getFixed(
179+
cast<FixedVectorType>(ValTy)->getNumElements()),
180+
ConstantInt::get(IType, 0x7c00))
181+
: ConstantInt::get(IType, 0x7c00);
182+
Constant *Zero =
183+
ValTy->isVectorTy()
184+
? ConstantVector::getSplat(
185+
ElementCount::getFixed(
186+
cast<FixedVectorType>(ValTy)->getNumElements()),
187+
ConstantInt::get(IType, 0))
188+
: ConstantInt::get(IType, 0);
189+
190+
Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
191+
Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
192+
Value *NotAllZeroes = Builder.CreateICmpNE(Exp, Zero);
193+
Value *NotAllOnes = Builder.CreateICmpNE(Exp, ExpBitMask);
194+
Value *B1 = Builder.CreateAnd(NotAllZeroes, NotAllOnes);
195+
return B1;
196+
}
197+
89198
static bool isIntrinsicExpansion(Function &F) {
90199
switch (F.getIntrinsicID()) {
91200
case Intrinsic::abs:
@@ -342,9 +451,11 @@ static Value *expandIsFPClass(CallInst *Orig) {
342451
case FPClassTest::fcInf:
343452
return expand16BitIsInf(Orig);
344453
case FPClassTest::fcNan:
454+
return expand16BitIsNaN(Orig);
345455
case FPClassTest::fcNormal:
456+
return expand16BitIsNormal(Orig);
346457
case FPClassTest::fcFinite:
347-
return nullptr;
458+
return expand16BitIsFinite(Orig);
348459
}
349460

350461
IRBuilder<> Builder(Orig);

llvm/test/CodeGen/DirectX/is_fpclass.ll

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ entry:
4646
ret i1 %0
4747
}
4848

49+
define noundef i1 @isnanh(half noundef %a) {
50+
; CHECK-LABEL: define noundef i1 @isnanh(
51+
; CHECK-SAME: half noundef [[A:%.*]]) {
52+
; CHECK-NEXT: [[ENTRY:.*:]]
53+
; SM69CHECK-NEXT: [[TMP0:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 8, half [[A]]) #[[ATTR0:[0-9]+]]
54+
; SMOLDCHECK-NEXT: [[BITCAST:%.*]] = bitcast half [[A]] to i16
55+
; SMOLDCHECK-NEXT: [[AND:%.*]] = and i16 [[BITCAST]], 31744
56+
; SMOLDCHECK-NEXT: [[CMPHIGH:%.*]] = icmp eq i16 [[AND]], 31744
57+
; SMOLDCHECK-NEXT: [[ANDLOW:%.*]] = and i16 [[BITCAST]], 1023
58+
; SMOLDCHECK-NEXT: [[CMPZERO:%.*]] = icmp ne i16 [[ANDLOW]], 0
59+
; SMOLDCHECK-NEXT: [[ANDLOW:%.*]] = and i1 [[CMPHIGH]], [[CMPZERO]]
60+
; SMOLDCHECK-NEXT: ret i1 [[ANDLOW]]
61+
; SM69CHECK-NEXT: ret i1 [[TMP0]]
62+
;
63+
entry:
64+
%0 = call i1 @llvm.is.fpclass.f16(half %a, i32 3)
65+
ret i1 %0
66+
}
67+
4968
define noundef <2 x i1> @isnanv2(<2 x float> noundef %a) {
5069
; CHECK-LABEL: define noundef <2 x i1> @isnanv2(
5170
; CHECK-SAME: <2 x float> noundef [[A:%.*]]) {
@@ -63,6 +82,40 @@ entry:
6382
ret <2 x i1> %0
6483
}
6584

85+
define noundef <2 x i1> @isnanhv2(<2 x half> noundef %a) {
86+
; CHECK-LABEL: define noundef <2 x i1> @isnanhv2(
87+
; CHECK-SAME: <2 x half> noundef [[A:%.*]]) {
88+
; CHECK-NEXT: [[ENTRY:.*:]]
89+
; CHECK-NEXT: [[A_I0:%.*]] = extractelement <2 x half> [[A]], i64 0
90+
; SM69CHECK-NEXT: [[DOTI02:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 8, half [[A_I0]]) #[[ATTR0:[0-9]+]]
91+
; SM69CHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
92+
; SM69CHECK-NEXT: [[DOTI11:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 8, half [[A_I1]]) #[[ATTR0]]
93+
; SM69CHECK-NEXT: [[DOTUPTO0:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI02]], i64 0
94+
; SM69CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO0]], i1 [[DOTI11]], i64 1
95+
; SM69CHECK-NEXT: ret <2 x i1> [[TMP0]]
96+
;
97+
; SMOLDCHECK-NEXT: [[DOTI0:%.*]] = bitcast half [[A_I0]] to i16
98+
; SMOLDCHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
99+
; SMOLDCHECK-NEXT: [[DOTI1:%.*]] = bitcast half [[A_I1]] to i16
100+
; SMOLDCHECK-NEXT: [[DOTI01:%.*]] = and i16 [[DOTI0]], 31744
101+
; SMOLDCHECK-NEXT: [[DOTI12:%.*]] = and i16 [[DOTI1]], 31744
102+
; SMOLDCHECK-NEXT: [[DOTI03:%.*]] = icmp eq i16 [[DOTI01]], 31744
103+
; SMOLDCHECK-NEXT: [[DOTI14:%.*]] = icmp eq i16 [[DOTI12]], 31744
104+
; SMOLDCHECK-NEXT: [[DOTI05:%.*]] = and i16 [[DOTI0]], 1023
105+
; SMOLDCHECK-NEXT: [[DOTI16:%.*]] = and i16 [[DOTI1]], 1023
106+
; SMOLDCHECK-NEXT: [[DOTI07:%.*]] = icmp ne i16 [[DOTI05]], 0
107+
; SMOLDCHECK-NEXT: [[DOTI18:%.*]] = icmp ne i16 [[DOTI16]], 0
108+
; SMOLDCHECK-NEXT: [[DOTI09:%.*]] = and i1 [[DOTI03]], [[DOTI07]]
109+
; SMOLDCHECK-NEXT: [[DOTI110:%.*]] = and i1 [[DOTI14]], [[DOTI18]]
110+
; SMOLDCHECK-NEXT: [[DOTUPTO015:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI09]], i64 0
111+
; SMOLDCHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO015]], i1 [[DOTI110]], i64 1
112+
; SMOLDCHECK-NEXT: ret <2 x i1> [[TMP0]]
113+
;
114+
entry:
115+
%0 = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> %a, i32 3)
116+
ret <2 x i1> %0
117+
}
118+
66119
define noundef i1 @isinf(float noundef %a) {
67120
; CHECK-LABEL: define noundef i1 @isinf(
68121
; CHECK-SAME: float noundef [[A:%.*]]) {
@@ -80,7 +133,7 @@ define noundef i1 @isinfh(half noundef %a) {
80133
; CHECK-SAME: half noundef [[A:%.*]]) {
81134
; CHECK-NEXT: [[ENTRY:.*:]]
82135
; SM69CHECK-NEXT: [[ISINF:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 9, half [[A]]) #[[ATTR0]]
83-
; SMOLDCHECK-NEXT: [[BITCAST:%.*]] = bitcast half %a to i16
136+
; SMOLDCHECK-NEXT: [[BITCAST:%.*]] = bitcast half [[A]] to i16
84137
; SMOLDCHECK-NEXT: [[CMPHIGH:%.*]] = icmp eq i16 [[BITCAST]], 31744
85138
; SMOLDCHECK-NEXT: [[CMPLOW:%.*]] = icmp eq i16 [[BITCAST]], -1024
86139
; SMOLDCHECK-NEXT: [[OR:%.*]] = or i1 [[CMPHIGH]], [[CMPLOW]]
@@ -121,6 +174,22 @@ entry:
121174
ret i1 %0
122175
}
123176

177+
define noundef i1 @isfiniteh(half noundef %a) {
178+
; CHECK-LABEL: define noundef i1 @isfiniteh(
179+
; CHECK-SAME: half noundef [[A:%.*]]) {
180+
; CHECK-NEXT: [[ENTRY:.*:]]
181+
; SM69CHECK-NEXT: [[TMP0:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 10, half [[A]]) #[[ATTR0]]
182+
; SMOLDCHECK-NEXT: [[BITCAST:%.*]] = bitcast half [[A]] to i16
183+
; SMOLDCHECK-NEXT: [[AND:%.*]] = and i16 [[BITCAST]], 31744
184+
; SMOLDCHECK-NEXT: [[CMPHIGH:%.*]] = icmp ne i16 [[AND]], 31744
185+
; SMOLDCHECK-NEXT: ret i1 [[CMPHIGH]]
186+
; SM69CHECK-NEXT: ret i1 [[TMP0]]
187+
;
188+
entry:
189+
%0 = call i1 @llvm.is.fpclass.f16(half %a, i32 504)
190+
ret i1 %0
191+
}
192+
124193
define noundef <2 x i1> @isfinitev2(<2 x float> noundef %a) {
125194
; CHECK-LABEL: define noundef <2 x i1> @isfinitev2(
126195
; CHECK-SAME: <2 x float> noundef [[A:%.*]]) {
@@ -138,6 +207,35 @@ entry:
138207
ret <2 x i1> %0
139208
}
140209

210+
define noundef <2 x i1> @isfinitehv2(<2 x half> noundef %a) {
211+
; CHECK-LABEL: define noundef <2 x i1> @isfinitehv2(
212+
; CHECK-SAME: <2 x half> noundef [[A:%.*]]) {
213+
; CHECK-NEXT: [[ENTRY:.*:]]
214+
; SM69CHECK-NEXT: [[A_I0:%.*]] = extractelement <2 x half> [[A]], i64 0
215+
; SM69CHECK-NEXT: [[DOTI02:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 10, half [[A_I0]]) #[[ATTR0:[0-9]+]]
216+
; SM69CHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
217+
; SM69CHECK-NEXT: [[DOTI11:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 10, half [[A_I1]]) #[[ATTR0]]
218+
; SM69CHECK-NEXT: [[DOTUPTO0:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI02]], i64 0
219+
; SM69CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO0]], i1 [[DOTI11]], i64 1
220+
; SM69CHECK-NEXT: ret <2 x i1> [[TMP0]]
221+
;
222+
; SMOLDCHECK-NEXT: [[A_I0:%.*]] = extractelement <2 x half> [[A]], i64 0
223+
; SMOLDCHECK-NEXT: [[DOTI0:%.*]] = bitcast half [[A_I0]] to i16
224+
; SMOLDCHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
225+
; SMOLDCHECK-NEXT: [[DOTI1:%.*]] = bitcast half [[A_I1]] to i16
226+
; SMOLDCHECK-NEXT: [[DOTI01:%.*]] = and i16 [[DOTI0]], 31744
227+
; SMOLDCHECK-NEXT: [[DOTI12:%.*]] = and i16 [[DOTI1]], 31744
228+
; SMOLDCHECK-NEXT: [[DOTI03:%.*]] = icmp ne i16 [[DOTI01]], 31744
229+
; SMOLDCHECK-NEXT: [[DOTI14:%.*]] = icmp ne i16 [[DOTI12]], 31744
230+
; SMOLDCHECK-NEXT: [[DOTUPTO06:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI03]], i64 0
231+
; SMOLDCHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO06]], i1 [[DOTI14]], i64 1
232+
; SMOLDCHECK-NEXT: ret <2 x i1> [[TMP0]]
233+
;
234+
entry:
235+
%0 = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> %a, i32 504)
236+
ret <2 x i1> %0
237+
}
238+
141239
define noundef i1 @isnormal(float noundef %a) {
142240
; CHECK-LABEL: define noundef i1 @isnormal(
143241
; CHECK-SAME: float noundef [[A:%.*]]) {
@@ -150,6 +248,24 @@ entry:
150248
ret i1 %0
151249
}
152250

251+
define noundef i1 @isnormalh(half noundef %a) {
252+
; CHECK-LABEL: define noundef i1 @isnormalh(
253+
; CHECK-SAME: half noundef [[A:%.*]]) {
254+
; CHECK-NEXT: [[ENTRY:.*:]]
255+
; SM69CHECK-NEXT: [[TMP0:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 11, half [[A]]) #[[ATTR0]]
256+
; SMOLDCHECK-NEXT: [[BITCAST:%.*]] = bitcast half [[A]] to i16
257+
; SMOLDCHECK-NEXT: [[AND:%.*]] = and i16 [[BITCAST]], 31744
258+
; SMOLDCHECK-NEXT: [[CMPZERO:%.*]] = icmp ne i16 [[AND]], 0
259+
; SMOLDCHECK-NEXT: [[CMPHIGH:%.*]] = icmp ne i16 [[AND]], 31744
260+
; SMOLDCHECK-NEXT: [[ANDCMP:%.*]] = and i1 [[CMPZERO]], [[CMPHIGH]]
261+
; SMOLDCHECK-NEXT: ret i1 [[ANDCMP]]
262+
; SM69CHECK-NEXT: ret i1 [[TMP0]]
263+
;
264+
entry:
265+
%0 = call i1 @llvm.is.fpclass.f16(half %a, i32 264)
266+
ret i1 %0
267+
}
268+
153269
define noundef <2 x i1> @isnormalv2(<2 x float> noundef %a) {
154270
; CHECK-LABEL: define noundef <2 x i1> @isnormalv2(
155271
; CHECK-SAME: <2 x float> noundef [[A:%.*]]) {
@@ -167,5 +283,37 @@ entry:
167283
ret <2 x i1> %0
168284
}
169285

286+
define noundef <2 x i1> @isnormalhv2(<2 x half> noundef %a) {
287+
; CHECK-LABEL: define noundef <2 x i1> @isnormalhv2(
288+
; CHECK-SAME: <2 x half> noundef [[A:%.*]]) {
289+
; CHECK-NEXT: [[ENTRY:.*:]]
290+
; CHECK-NEXT: [[A_I0:%.*]] = extractelement <2 x half> [[A]], i64 0
291+
; SM69CHECK-NEXT: [[DOTI02:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 11, half [[A_I0]]) #[[ATTR0:[0-9]+]]
292+
; SM69CHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
293+
; SM69CHECK-NEXT: [[DOTI11:%.*]] = call i1 @dx.op.isSpecialFloat.f16(i32 11, half [[A_I1]]) #[[ATTR0]]
294+
; SM69CHECK-NEXT: [[DOTUPTO0:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI02]], i64 0
295+
; SM69CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO0]], i1 [[DOTI11]], i64 1
296+
; SM69CHECK-NEXT: ret <2 x i1> [[TMP0]]
297+
;
298+
; SMOLDCHECK-NEXT: [[DOTI0:%.*]] = bitcast half [[A_I0]] to i16
299+
; SMOLDCHECK-NEXT: [[A_I1:%.*]] = extractelement <2 x half> [[A]], i64 1
300+
; SMOLDCHECK-NEXT: [[DOTI1:%.*]] = bitcast half [[A_I1]] to i16
301+
; SMOLDCHECK-NEXT: [[DOTI01:%.*]] = and i16 [[DOTI0]], 31744
302+
; SMOLDCHECK-NEXT: [[DOTI12:%.*]] = and i16 [[DOTI1]], 31744
303+
; SMOLDCHECK-NEXT: [[DOTI03:%.*]] = icmp ne i16 [[DOTI01]], 0
304+
; SMOLDCHECK-NEXT: [[DOTI14:%.*]] = icmp ne i16 [[DOTI12]], 0
305+
; SMOLDCHECK-NEXT: [[DOTI05:%.*]] = icmp ne i16 [[DOTI01]], 31744
306+
; SMOLDCHECK-NEXT: [[DOTI16:%.*]] = icmp ne i16 [[DOTI12]], 31744
307+
; SMOLDCHECK-NEXT: [[DOTI07:%.*]] = and i1 [[DOTI03]], [[DOTI05]]
308+
; SMOLDCHECK-NEXT: [[DOTI18:%.*]] = and i1 [[DOTI14]], [[DOTI16]]
309+
; SMOLDCHECK-NEXT: [[DOTUPTO012:%.*]] = insertelement <2 x i1> poison, i1 [[DOTI07]], i64 0
310+
; SMOLDCHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i1> [[DOTUPTO012]], i1 [[DOTI18]], i64 1
311+
; SMOLDCHECK-NEXT: ret <2 x i1> [[TMP0]]
312+
;
313+
entry:
314+
%0 = call <2 x i1> @llvm.is.fpclass.v2f16(<2 x half> %a, i32 264)
315+
ret <2 x i1> %0
316+
}
317+
170318
declare i1 @llvm.is.fpclass.f32(float, i32 immarg)
171319
declare <2 x i1> @llvm.is.fpclass.v2f32(<2 x float>, i32 immarg)

0 commit comments

Comments
 (0)