@@ -4332,6 +4332,204 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
43324332 return BM->addInstTemplate (internal::OpMaskedScatterINTEL, Ops, BB,
43334333 nullptr );
43344334 }
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+ }
43354533
43364534 default :
43374535 if (BM->isUnknownIntrinsicAllowed (II))
0 commit comments