Skip to content

Commit 9e72f1d

Browse files
MrSidimsagainull
authored andcommitted
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" ( 08f0388 ) 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 <[email protected]> Original commit: KhronosGroup/SPIRV-LLVM-Translator@0617edd
1 parent d965d9a commit 9e72f1d

File tree

2 files changed

+655
-0
lines changed

2 files changed

+655
-0
lines changed

llvm-spirv/lib/SPIRV/SPIRVWriter.cpp

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

Comments
 (0)