@@ -4332,6 +4332,204 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
4332
4332
return BM->addInstTemplate (internal::OpMaskedScatterINTEL, Ops, BB,
4333
4333
nullptr );
4334
4334
}
4335
+ case Intrinsic::is_fpclass: {
4336
+ // There is no direct counterpart for the intrinsic in SPIR-V, hence
4337
+ // we need to emulate its work by sequence of other instructions
4338
+ SPIRVType *ResTy = transType (II->getType ());
4339
+ llvm::FPClassTest FPClass = static_cast <llvm::FPClassTest>(
4340
+ cast<ConstantInt>(II->getArgOperand (1 ))->getZExtValue ());
4341
+ // if no tests are provided - return false
4342
+ if (FPClass == 0 )
4343
+ return BM->addConstant (ResTy, false );
4344
+ // if all tests are provided - return true
4345
+ if (FPClass == fcAllFlags)
4346
+ return BM->addConstant (ResTy, true );
4347
+
4348
+ Type *OpLLVMTy = II->getArgOperand (0 )->getType ();
4349
+ SPIRVValue *InputFloat = transValue (II->getArgOperand (0 ), BB);
4350
+ std::vector<SPIRVValue *> ResultVec;
4351
+
4352
+ // Adds test for Negative/Positive values
4353
+ SPIRVValue *SignBitTest = nullptr ;
4354
+ SPIRVValue *NoSignTest = nullptr ;
4355
+ auto GetNegPosInstTest = [&](SPIRVValue *TestInst,
4356
+ bool IsNegative) -> SPIRVValue * {
4357
+ SignBitTest = (SignBitTest)
4358
+ ? SignBitTest
4359
+ : BM->addInstTemplate (OpSignBitSet,
4360
+ {InputFloat->getId ()}, BB, ResTy);
4361
+ if (IsNegative) {
4362
+ return BM->addInstTemplate (
4363
+ OpLogicalAnd, {SignBitTest->getId (), TestInst->getId ()}, BB, ResTy);
4364
+ }
4365
+ NoSignTest = (NoSignTest)
4366
+ ? NoSignTest
4367
+ : BM->addInstTemplate (OpLogicalNot,
4368
+ {SignBitTest->getId ()}, BB, ResTy);
4369
+ return BM->addInstTemplate (
4370
+ OpLogicalAnd, {NoSignTest->getId (), TestInst->getId ()}, BB, ResTy);
4371
+ };
4372
+
4373
+ // Get LLVM Op type converted to integer. It can be either scalar or vector.
4374
+ const uint32_t BitSize = OpLLVMTy->getScalarSizeInBits ();
4375
+ Type *IntOpLLVMTy = IntegerType::getIntNTy (M->getContext (), BitSize);
4376
+ if (OpLLVMTy->isVectorTy ())
4377
+ IntOpLLVMTy = FixedVectorType::get (
4378
+ IntOpLLVMTy, cast<FixedVectorType>(OpLLVMTy)->getNumElements ());
4379
+ SPIRVType *OpSPIRVTy = transType (IntOpLLVMTy);
4380
+ const llvm::fltSemantics &Semantics =
4381
+ OpLLVMTy->getScalarType ()->getFltSemantics ();
4382
+ const APInt Inf = APFloat::getInf (Semantics).bitcastToAPInt ();
4383
+ const APInt AllOneMantissa =
4384
+ APFloat::getLargest (Semantics).bitcastToAPInt () & ~Inf;
4385
+
4386
+ // Some checks can be inverted tests for simple cases, for example
4387
+ // simultaneous check for inf, normal, subnormal and zero is a check for
4388
+ // non nan.
4389
+ auto GetInvertedFPClassTest =
4390
+ [](const llvm::FPClassTest Test) -> llvm::FPClassTest {
4391
+ llvm::FPClassTest InvertedTest = ~Test & fcAllFlags;
4392
+ switch (InvertedTest) {
4393
+ default :
4394
+ break ;
4395
+ case fcNan:
4396
+ case fcSNan:
4397
+ case fcQNan:
4398
+ case fcInf:
4399
+ case fcPosInf:
4400
+ case fcNegInf:
4401
+ case fcNormal:
4402
+ case fcPosNormal:
4403
+ case fcNegNormal:
4404
+ case fcSubnormal:
4405
+ case fcPosSubnormal:
4406
+ case fcNegSubnormal:
4407
+ case fcZero:
4408
+ case fcPosZero:
4409
+ case fcNegZero:
4410
+ case fcFinite:
4411
+ case fcPosFinite:
4412
+ case fcNegFinite:
4413
+ return InvertedTest;
4414
+ }
4415
+ return fcNone;
4416
+ };
4417
+ bool IsInverted = false ;
4418
+ if (llvm::FPClassTest InvertedCheck = GetInvertedFPClassTest (FPClass)) {
4419
+ IsInverted = true ;
4420
+ FPClass = InvertedCheck;
4421
+ }
4422
+ auto GetInvertedTestIfNeeded = [&](SPIRVValue *TestInst) -> SPIRVValue * {
4423
+ if (!IsInverted)
4424
+ return TestInst;
4425
+ return BM->addInstTemplate (OpLogicalNot, {TestInst->getId ()}, BB, ResTy);
4426
+ };
4427
+
4428
+ // Integer parameter of the intrinsic is combined from several bit masks
4429
+ // referenced in FPClassTest enum from FloatingPointMode.h in LLVM.
4430
+ // Since a single intrinsic can provide multiple tests - here we might end
4431
+ // up adding several sequences of SPIR-V instructions
4432
+ if (FPClass & fcNan) {
4433
+ // Map on OpIsNan if we have both QNan and SNan test bits set
4434
+ if (FPClass & fcSNan && FPClass & fcQNan) {
4435
+ auto *TestIsNan =
4436
+ BM->addInstTemplate (OpIsNan, {InputFloat->getId ()}, BB, ResTy);
4437
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsNan));
4438
+ } else {
4439
+ // isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit)
4440
+ APInt QNaNBitMask =
4441
+ APInt::getOneBitSet (BitSize, AllOneMantissa.getActiveBits () - 1 );
4442
+ APInt InfWithQnanBit = Inf | QNaNBitMask;
4443
+ auto *QNanBitConst = transValue (
4444
+ Constant::getIntegerValue (IntOpLLVMTy, InfWithQnanBit), BB);
4445
+ auto *BitCastToInt =
4446
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4447
+ auto *IntAbs =
4448
+ BM->addExtInst (OpSPIRVTy, BM->getExtInstSetId (SPIRVEIS_OpenCL),
4449
+ OpenCLLIB::SAbs, {BitCastToInt}, BB);
4450
+ auto *TestIsQNan = BM->addCmpInst (OpUGreaterThanEqual, ResTy, IntAbs,
4451
+ QNanBitConst, BB);
4452
+ if (FPClass & fcQNan) {
4453
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsQNan));
4454
+ } else {
4455
+ // issignaling(V) ==> isnan(V) && !isquiet(V)
4456
+ auto *TestIsNan =
4457
+ BM->addInstTemplate (OpIsNan, {InputFloat->getId ()}, BB, ResTy);
4458
+ auto *NotQNan = BM->addInstTemplate (OpLogicalNot,
4459
+ {TestIsQNan->getId ()}, BB, ResTy);
4460
+ auto *TestIsSNan = BM->addInstTemplate (
4461
+ OpLogicalAnd, {TestIsNan->getId (), NotQNan->getId ()}, BB, ResTy);
4462
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsSNan));
4463
+ }
4464
+ }
4465
+ }
4466
+ if (FPClass & fcInf) {
4467
+ auto *TestIsInf =
4468
+ BM->addInstTemplate (OpIsInf, {InputFloat->getId ()}, BB, ResTy);
4469
+ if (FPClass & fcNegInf && FPClass & fcPosInf)
4470
+ // Map on OpIsInf if we have both Inf test bits set
4471
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsInf));
4472
+ else
4473
+ // Map on OpIsInf with following check for sign bit
4474
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4475
+ GetNegPosInstTest (TestIsInf, FPClass & fcNegInf)));
4476
+ }
4477
+ if (FPClass & fcNormal) {
4478
+ auto *TestIsNormal =
4479
+ BM->addInstTemplate (OpIsNormal, {InputFloat->getId ()}, BB, ResTy);
4480
+ if (FPClass & fcNegNormal && FPClass & fcPosNormal)
4481
+ // Map on OpIsNormal if we have both Normal test bits set
4482
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsNormal));
4483
+ else
4484
+ // Map on OpIsNormal with following check for sign bit
4485
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4486
+ GetNegPosInstTest (TestIsNormal, FPClass & fcNegNormal)));
4487
+ }
4488
+ if (FPClass & fcSubnormal) {
4489
+ // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set)
4490
+ auto *BitCastToInt =
4491
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4492
+ SPIRVValue *IntAbs =
4493
+ BM->addExtInst (OpSPIRVTy, BM->getExtInstSetId (SPIRVEIS_OpenCL),
4494
+ OpenCLLIB::SAbs, {BitCastToInt}, BB);
4495
+ auto *MantissaConst = transValue (
4496
+ Constant::getIntegerValue (IntOpLLVMTy, AllOneMantissa), BB);
4497
+ auto *MinusOne =
4498
+ BM->addBinaryInst (OpISub, OpSPIRVTy, IntAbs, MantissaConst, BB);
4499
+ auto *TestIsSubnormal =
4500
+ BM->addCmpInst (OpULessThan, ResTy, MinusOne, MantissaConst, BB);
4501
+ if (FPClass & fcPosSubnormal && FPClass & fcNegSubnormal)
4502
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsSubnormal));
4503
+ else
4504
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4505
+ GetNegPosInstTest (TestIsSubnormal, FPClass & fcNegSubnormal)));
4506
+ }
4507
+ if (FPClass & fcZero) {
4508
+ // Create zero integer constant and check for equality with bitcasted to
4509
+ // int float value
4510
+ auto *BitCastToInt =
4511
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4512
+ auto *ZeroConst = transValue (
4513
+ Constant::getIntegerValue (IntOpLLVMTy, APInt::getZero (BitSize)), BB);
4514
+ auto *TestIsZero =
4515
+ BM->addCmpInst (OpIEqual, ResTy, BitCastToInt, ZeroConst, BB);
4516
+ if (FPClass & fcPosZero && FPClass & fcNegZero)
4517
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsZero));
4518
+ else
4519
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4520
+ GetNegPosInstTest (TestIsZero, FPClass & fcNegZero)));
4521
+ }
4522
+ if (ResultVec.size () == 1 )
4523
+ return ResultVec.back ();
4524
+ SPIRVValue *Result = ResultVec.front ();
4525
+ for (size_t I = 1 ; I != ResultVec.size (); ++I) {
4526
+ // Create a sequence of LogicalOr instructions from ResultVec to get
4527
+ // the overall test result
4528
+ std::vector<SPIRVId> LogicOps = {Result->getId (), ResultVec[I]->getId ()};
4529
+ Result = BM->addInstTemplate (OpLogicalOr, LogicOps, BB, ResTy);
4530
+ }
4531
+ return Result;
4532
+ }
4335
4533
4336
4534
default :
4337
4535
if (BM->isUnknownIntrinsicAllowed (II))
0 commit comments