Skip to content

Commit 0617edd

Browse files
authored
Add translation of fpclass llvm intrinsic (#1922)
Optimized LLVM IR started to contain this intrinsic recently after InstCombine updates, like: "Fold and/or of fcmp into class" ( 08f03887 ) There is no direct counterpart for it in SPIR-V, so testing is mapped on a sequence of SPIR-V instructions: test for both qnan and snan -> OpIsNan test for either of qnan and snan -> isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit) issignaling(V) ==> isnan(V) && !isquiet(V) test for neg/pos inf -> OpIsInf + OpSignBitSet test for neg/pos normal -> OpIsNormal + OpSignBitSet test for neg/pos subnormal -> issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set) test for neg/pos zero -> compare bitcasted to int float with 0 + OpSignBitSet Signed-off-by: Sidorov, Dmitry <dmitry.sidorov@intel.com>
1 parent 66b7618 commit 0617edd

File tree

2 files changed

+655
-0
lines changed

2 files changed

+655
-0
lines changed

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)