@@ -4399,6 +4399,204 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
43994399 return BM->addInstTemplate (internal::OpMaskedScatterINTEL, Ops, BB,
44004400 nullptr );
44014401 }
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+ }
44024600
44034601 default :
44044602 if (BM->isUnknownIntrinsicAllowed (II))
0 commit comments