@@ -4399,6 +4399,204 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
4399
4399
return BM->addInstTemplate (internal::OpMaskedScatterINTEL, Ops, BB,
4400
4400
nullptr );
4401
4401
}
4402
+ case Intrinsic::is_fpclass: {
4403
+ // There is no direct counterpart for the intrinsic in SPIR-V, hence
4404
+ // we need to emulate its work by sequence of other instructions
4405
+ SPIRVType *ResTy = transType (II->getType ());
4406
+ llvm::FPClassTest FPClass = static_cast <llvm::FPClassTest>(
4407
+ cast<ConstantInt>(II->getArgOperand (1 ))->getZExtValue ());
4408
+ // if no tests are provided - return false
4409
+ if (FPClass == 0 )
4410
+ return BM->addConstant (ResTy, false );
4411
+ // if all tests are provided - return true
4412
+ if (FPClass == fcAllFlags)
4413
+ return BM->addConstant (ResTy, true );
4414
+
4415
+ Type *OpLLVMTy = II->getArgOperand (0 )->getType ();
4416
+ SPIRVValue *InputFloat = transValue (II->getArgOperand (0 ), BB);
4417
+ std::vector<SPIRVValue *> ResultVec;
4418
+
4419
+ // Adds test for Negative/Positive values
4420
+ SPIRVValue *SignBitTest = nullptr ;
4421
+ SPIRVValue *NoSignTest = nullptr ;
4422
+ auto GetNegPosInstTest = [&](SPIRVValue *TestInst,
4423
+ bool IsNegative) -> SPIRVValue * {
4424
+ SignBitTest = (SignBitTest)
4425
+ ? SignBitTest
4426
+ : BM->addInstTemplate (OpSignBitSet,
4427
+ {InputFloat->getId ()}, BB, ResTy);
4428
+ if (IsNegative) {
4429
+ return BM->addInstTemplate (
4430
+ OpLogicalAnd, {SignBitTest->getId (), TestInst->getId ()}, BB, ResTy);
4431
+ }
4432
+ NoSignTest = (NoSignTest)
4433
+ ? NoSignTest
4434
+ : BM->addInstTemplate (OpLogicalNot,
4435
+ {SignBitTest->getId ()}, BB, ResTy);
4436
+ return BM->addInstTemplate (
4437
+ OpLogicalAnd, {NoSignTest->getId (), TestInst->getId ()}, BB, ResTy);
4438
+ };
4439
+
4440
+ // Get LLVM Op type converted to integer. It can be either scalar or vector.
4441
+ const uint32_t BitSize = OpLLVMTy->getScalarSizeInBits ();
4442
+ Type *IntOpLLVMTy = IntegerType::getIntNTy (M->getContext (), BitSize);
4443
+ if (OpLLVMTy->isVectorTy ())
4444
+ IntOpLLVMTy = FixedVectorType::get (
4445
+ IntOpLLVMTy, cast<FixedVectorType>(OpLLVMTy)->getNumElements ());
4446
+ SPIRVType *OpSPIRVTy = transType (IntOpLLVMTy);
4447
+ const llvm::fltSemantics &Semantics =
4448
+ OpLLVMTy->getScalarType ()->getFltSemantics ();
4449
+ const APInt Inf = APFloat::getInf (Semantics).bitcastToAPInt ();
4450
+ const APInt AllOneMantissa =
4451
+ APFloat::getLargest (Semantics).bitcastToAPInt () & ~Inf;
4452
+
4453
+ // Some checks can be inverted tests for simple cases, for example
4454
+ // simultaneous check for inf, normal, subnormal and zero is a check for
4455
+ // non nan.
4456
+ auto GetInvertedFPClassTest =
4457
+ [](const llvm::FPClassTest Test) -> llvm::FPClassTest {
4458
+ llvm::FPClassTest InvertedTest = ~Test & fcAllFlags;
4459
+ switch (InvertedTest) {
4460
+ default :
4461
+ break ;
4462
+ case fcNan:
4463
+ case fcSNan:
4464
+ case fcQNan:
4465
+ case fcInf:
4466
+ case fcPosInf:
4467
+ case fcNegInf:
4468
+ case fcNormal:
4469
+ case fcPosNormal:
4470
+ case fcNegNormal:
4471
+ case fcSubnormal:
4472
+ case fcPosSubnormal:
4473
+ case fcNegSubnormal:
4474
+ case fcZero:
4475
+ case fcPosZero:
4476
+ case fcNegZero:
4477
+ case fcFinite:
4478
+ case fcPosFinite:
4479
+ case fcNegFinite:
4480
+ return InvertedTest;
4481
+ }
4482
+ return fcNone;
4483
+ };
4484
+ bool IsInverted = false ;
4485
+ if (llvm::FPClassTest InvertedCheck = GetInvertedFPClassTest (FPClass)) {
4486
+ IsInverted = true ;
4487
+ FPClass = InvertedCheck;
4488
+ }
4489
+ auto GetInvertedTestIfNeeded = [&](SPIRVValue *TestInst) -> SPIRVValue * {
4490
+ if (!IsInverted)
4491
+ return TestInst;
4492
+ return BM->addInstTemplate (OpLogicalNot, {TestInst->getId ()}, BB, ResTy);
4493
+ };
4494
+
4495
+ // Integer parameter of the intrinsic is combined from several bit masks
4496
+ // referenced in FPClassTest enum from FloatingPointMode.h in LLVM.
4497
+ // Since a single intrinsic can provide multiple tests - here we might end
4498
+ // up adding several sequences of SPIR-V instructions
4499
+ if (FPClass & fcNan) {
4500
+ // Map on OpIsNan if we have both QNan and SNan test bits set
4501
+ if (FPClass & fcSNan && FPClass & fcQNan) {
4502
+ auto *TestIsNan =
4503
+ BM->addInstTemplate (OpIsNan, {InputFloat->getId ()}, BB, ResTy);
4504
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsNan));
4505
+ } else {
4506
+ // isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit)
4507
+ APInt QNaNBitMask =
4508
+ APInt::getOneBitSet (BitSize, AllOneMantissa.getActiveBits () - 1 );
4509
+ APInt InfWithQnanBit = Inf | QNaNBitMask;
4510
+ auto *QNanBitConst = transValue (
4511
+ Constant::getIntegerValue (IntOpLLVMTy, InfWithQnanBit), BB);
4512
+ auto *BitCastToInt =
4513
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4514
+ auto *IntAbs =
4515
+ BM->addExtInst (OpSPIRVTy, BM->getExtInstSetId (SPIRVEIS_OpenCL),
4516
+ OpenCLLIB::SAbs, {BitCastToInt}, BB);
4517
+ auto *TestIsQNan = BM->addCmpInst (OpUGreaterThanEqual, ResTy, IntAbs,
4518
+ QNanBitConst, BB);
4519
+ if (FPClass & fcQNan) {
4520
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsQNan));
4521
+ } else {
4522
+ // issignaling(V) ==> isnan(V) && !isquiet(V)
4523
+ auto *TestIsNan =
4524
+ BM->addInstTemplate (OpIsNan, {InputFloat->getId ()}, BB, ResTy);
4525
+ auto *NotQNan = BM->addInstTemplate (OpLogicalNot,
4526
+ {TestIsQNan->getId ()}, BB, ResTy);
4527
+ auto *TestIsSNan = BM->addInstTemplate (
4528
+ OpLogicalAnd, {TestIsNan->getId (), NotQNan->getId ()}, BB, ResTy);
4529
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsSNan));
4530
+ }
4531
+ }
4532
+ }
4533
+ if (FPClass & fcInf) {
4534
+ auto *TestIsInf =
4535
+ BM->addInstTemplate (OpIsInf, {InputFloat->getId ()}, BB, ResTy);
4536
+ if (FPClass & fcNegInf && FPClass & fcPosInf)
4537
+ // Map on OpIsInf if we have both Inf test bits set
4538
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsInf));
4539
+ else
4540
+ // Map on OpIsInf with following check for sign bit
4541
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4542
+ GetNegPosInstTest (TestIsInf, FPClass & fcNegInf)));
4543
+ }
4544
+ if (FPClass & fcNormal) {
4545
+ auto *TestIsNormal =
4546
+ BM->addInstTemplate (OpIsNormal, {InputFloat->getId ()}, BB, ResTy);
4547
+ if (FPClass & fcNegNormal && FPClass & fcPosNormal)
4548
+ // Map on OpIsNormal if we have both Normal test bits set
4549
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsNormal));
4550
+ else
4551
+ // Map on OpIsNormal with following check for sign bit
4552
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4553
+ GetNegPosInstTest (TestIsNormal, FPClass & fcNegNormal)));
4554
+ }
4555
+ if (FPClass & fcSubnormal) {
4556
+ // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set)
4557
+ auto *BitCastToInt =
4558
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4559
+ SPIRVValue *IntAbs =
4560
+ BM->addExtInst (OpSPIRVTy, BM->getExtInstSetId (SPIRVEIS_OpenCL),
4561
+ OpenCLLIB::SAbs, {BitCastToInt}, BB);
4562
+ auto *MantissaConst = transValue (
4563
+ Constant::getIntegerValue (IntOpLLVMTy, AllOneMantissa), BB);
4564
+ auto *MinusOne =
4565
+ BM->addBinaryInst (OpISub, OpSPIRVTy, IntAbs, MantissaConst, BB);
4566
+ auto *TestIsSubnormal =
4567
+ BM->addCmpInst (OpULessThan, ResTy, MinusOne, MantissaConst, BB);
4568
+ if (FPClass & fcPosSubnormal && FPClass & fcNegSubnormal)
4569
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsSubnormal));
4570
+ else
4571
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4572
+ GetNegPosInstTest (TestIsSubnormal, FPClass & fcNegSubnormal)));
4573
+ }
4574
+ if (FPClass & fcZero) {
4575
+ // Create zero integer constant and check for equality with bitcasted to
4576
+ // int float value
4577
+ auto *BitCastToInt =
4578
+ BM->addUnaryInst (OpBitcast, OpSPIRVTy, InputFloat, BB);
4579
+ auto *ZeroConst = transValue (
4580
+ Constant::getIntegerValue (IntOpLLVMTy, APInt::getZero (BitSize)), BB);
4581
+ auto *TestIsZero =
4582
+ BM->addCmpInst (OpIEqual, ResTy, BitCastToInt, ZeroConst, BB);
4583
+ if (FPClass & fcPosZero && FPClass & fcNegZero)
4584
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (TestIsZero));
4585
+ else
4586
+ ResultVec.emplace_back (GetInvertedTestIfNeeded (
4587
+ GetNegPosInstTest (TestIsZero, FPClass & fcNegZero)));
4588
+ }
4589
+ if (ResultVec.size () == 1 )
4590
+ return ResultVec.back ();
4591
+ SPIRVValue *Result = ResultVec.front ();
4592
+ for (size_t I = 1 ; I != ResultVec.size (); ++I) {
4593
+ // Create a sequence of LogicalOr instructions from ResultVec to get
4594
+ // the overall test result
4595
+ std::vector<SPIRVId> LogicOps = {Result->getId (), ResultVec[I]->getId ()};
4596
+ Result = BM->addInstTemplate (OpLogicalOr, LogicOps, BB, ResTy);
4597
+ }
4598
+ return Result;
4599
+ }
4402
4600
4403
4601
default :
4404
4602
if (BM->isUnknownIntrinsicAllowed (II))
0 commit comments