Skip to content

Commit b56797f

Browse files
asudarsajsji
authored andcommitted
fpbuiltin-max-error support (#2056)
This changes add SPIR-V translator support for the SPIR-V extension documented here: KhronosGroup/SPIRV-Registry#193. This extension adds one decoration to represent maximum error for FP operations and adds the related Capability. SPIRV Headers support for representing this in SPIR-V: KhronosGroup/SPIRV-Headers#363 #8134 added a new call-site attribute associated with FP builtin intrinsics. This attribute is named 'fpbuiltin-max-error'. Following example shows how this extension is supported in the translator. The input LLVM IR uses new LLVM builtin calls to represent FP operations. An attribute named 'fpbuiltin-max-error' is used to represent the max-error allowed in the FP operation. Example Input LLVM: %t6 = call float @llvm.fpbuiltin.sin.f32(float %f1) #2 attributes #2 = { "fpbuiltin-max-error"="2.5" } This is translated into a SPIR-V instruction (for add/sub/mul/div/rem) and OpenCl extended instruction for other instructions. A decoration to represent the max-error is attached to the SPIR-V instruction. SPIR-V code: 4 Decorate 97 FPMaxErrorDecorationINTEL 1075838976 6 ExtInst 2 97 1 sin 88 No new support is added to support translating this SPIR_V back to LLVM. Existing support is used. The decoration is translated back into named metadata associated with the LLVM instruction. This can be readily consumed by backends. Based on input from @andykaylor, we emit attributes when the FP operation is translated back to a call to a builtin function and emit metadata otherwise. Translated LLVM code for basic math functions (add/sub/mul/div/rem): %t6 = fmul float %f1, %f2, !fpbuiltin-max-error !7 !7 = !{!"2.500000"} Translated LLVM code for other math functions: %t6 = call spir_func float @_Z3sinf(float %f1) #3 attributes #3 = { "fpbuiltin-max-error"="4.000000" } Signed-off-by: Arvind Sudarsanam <arvind.sudarsanam@intel.com> Original commit: KhronosGroup/SPIRV-LLVM-Translator@c6fe12b
1 parent 10ed9a1 commit b56797f

File tree

10 files changed

+464
-2
lines changed

10 files changed

+464
-2
lines changed

llvm-spirv/include/LLVMSPIRVExtensions.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ EXT(SPV_INTEL_tensor_float32_rounding)
6363
EXT(SPV_EXT_relaxed_printf_string_address_space)
6464
EXT(SPV_INTEL_fpga_argument_interfaces)
6565
EXT(SPV_INTEL_fpga_latency_control)
66+
EXT(SPV_INTEL_fp_max_error)
6667
EXT(SPV_INTEL_cache_controls)

llvm-spirv/lib/SPIRV/SPIRVBuiltinHelper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ Value *BuiltinCallMutator::doConversion() {
102102
CallInst *NewCall =
103103
Builder.Insert(addCallInst(CI->getModule(), FuncName, ReturnTy, Args,
104104
&Attrs, nullptr, Mangler.get()));
105+
NewCall->copyMetadata(*CI);
106+
NewCall->setAttributes(CI->getAttributes());
105107
Value *Result = MutateRet ? MutateRet(Builder, NewCall) : NewCall;
106108
Result->takeName(CI);
107109
if (!CI->getType()->isVoidTy())

llvm-spirv/lib/SPIRV/SPIRVReader.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,7 +3868,48 @@ void SPIRVToLLVM::transDecorationsToMetadata(SPIRVValue *BV, Value *V) {
38683868
SetDecorationsMetadata(I);
38693869
}
38703870

3871+
namespace {
3872+
3873+
static float convertSPIRVWordToFloat(SPIRVWord Spir) {
3874+
union {
3875+
float F;
3876+
SPIRVWord Spir;
3877+
} FPMaxError;
3878+
FPMaxError.Spir = Spir;
3879+
return FPMaxError.F;
3880+
}
3881+
3882+
static bool transFPMaxErrorDecoration(SPIRVValue *BV, Value *V,
3883+
LLVMContext *Context) {
3884+
SPIRVWord ID;
3885+
if (Instruction *I = dyn_cast<Instruction>(V))
3886+
if (BV->hasDecorate(DecorationFPMaxErrorDecorationINTEL, 0, &ID)) {
3887+
auto Literals =
3888+
BV->getDecorationLiterals(DecorationFPMaxErrorDecorationINTEL);
3889+
assert(Literals.size() == 1 &&
3890+
"FP Max Error decoration shall have 1 operand");
3891+
auto F = convertSPIRVWordToFloat(Literals[0]);
3892+
if (CallInst *CI = dyn_cast<CallInst>(I)) {
3893+
// Add attribute
3894+
auto A = llvm::Attribute::get(*Context, "fpbuiltin-max-error",
3895+
std::to_string(F));
3896+
CI->addFnAttr(A);
3897+
} else {
3898+
// Add metadata
3899+
MDNode *N =
3900+
MDNode::get(*Context, MDString::get(*Context, std::to_string(F)));
3901+
I->setMetadata("fpbuiltin-max-error", N);
3902+
}
3903+
return true;
3904+
}
3905+
return false;
3906+
}
3907+
} // namespace
3908+
38713909
bool SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) {
3910+
if (transFPMaxErrorDecoration(BV, V, Context))
3911+
return true;
3912+
38723913
if (!transAlign(BV, V))
38733914
return false;
38743915

llvm-spirv/lib/SPIRV/SPIRVWriter.cpp

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ using namespace llvm;
106106
using namespace SPIRV;
107107
using namespace OCLUtil;
108108

109+
namespace {
110+
111+
static SPIRVWord convertFloatToSPIRVWord(float F) {
112+
union {
113+
float F;
114+
SPIRVWord Spir;
115+
} FPMaxError;
116+
FPMaxError.F = F;
117+
return FPMaxError.Spir;
118+
}
119+
120+
} // namespace
121+
109122
namespace SPIRV {
110123

111124
static void foreachKernelArgMD(
@@ -3545,6 +3558,26 @@ bool LLVMToSPIRVBase::isKnownIntrinsic(Intrinsic::ID Id) {
35453558
}
35463559
}
35473560

3561+
// Add decoration if needed
3562+
SPIRVInstruction *addFPBuiltinDecoration(SPIRVModule *BM, IntrinsicInst *II,
3563+
SPIRVInstruction *I) {
3564+
const bool AllowFPMaxError =
3565+
BM->isAllowedToUseExtension(ExtensionID::SPV_INTEL_fp_max_error);
3566+
assert(II->getCalledFunction()->getName().startswith("llvm.fpbuiltin"));
3567+
// Add a new decoration for llvm.builtin intrinsics, if needed
3568+
if (AllowFPMaxError)
3569+
if (II->getAttributes().hasFnAttr("fpbuiltin-max-error")) {
3570+
double F = 0.0;
3571+
II->getAttributes()
3572+
.getFnAttr("fpbuiltin-max-error")
3573+
.getValueAsString()
3574+
.getAsDouble(F);
3575+
I->addDecorate(DecorationFPMaxErrorDecorationINTEL,
3576+
convertFloatToSPIRVWord(F));
3577+
}
3578+
return I;
3579+
}
3580+
35483581
// Performs mapping of LLVM IR rounding mode to SPIR-V rounding mode
35493582
// Value *V is metadata <rounding mode> argument of
35503583
// llvm.experimental.constrained.* intrinsics
@@ -4488,8 +4521,9 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
44884521
}
44894522
return Result;
44904523
}
4491-
44924524
default:
4525+
if (auto *BVar = transFPBuiltinIntrinsicInst(II, BB))
4526+
return BVar;
44934527
if (BM->isUnknownIntrinsicAllowed(II))
44944528
return BM->addCallInst(
44954529
transFunctionDecl(II->getCalledFunction()),
@@ -4505,6 +4539,124 @@ SPIRVValue *LLVMToSPIRVBase::transIntrinsicInst(IntrinsicInst *II,
45054539
return nullptr;
45064540
}
45074541

4542+
LLVMToSPIRVBase::FPBuiltinType
4543+
LLVMToSPIRVBase::getFPBuiltinType(IntrinsicInst *II, StringRef &OpName) {
4544+
StringRef Name = II->getCalledFunction()->getName();
4545+
if (!Name.startswith("llvm.fpbuiltin"))
4546+
return FPBuiltinType::UNKNOWN;
4547+
Name.consume_front("llvm.fpbuiltin.");
4548+
OpName = Name.split('.').first;
4549+
FPBuiltinType Type =
4550+
StringSwitch<FPBuiltinType>(OpName)
4551+
.Cases("fadd", "fsub", "fmul", "fdiv", "frem",
4552+
FPBuiltinType::REGULAR_MATH)
4553+
.Cases("sin", "cos", "tan", FPBuiltinType::EXT_1OPS)
4554+
.Cases("sinh", "cosh", "tanh", FPBuiltinType::EXT_1OPS)
4555+
.Cases("asin", "acos", "atan", FPBuiltinType::EXT_1OPS)
4556+
.Cases("asinh", "acosh", "atanh", FPBuiltinType::EXT_1OPS)
4557+
.Cases("exp", "exp2", "exp10", "expm1", FPBuiltinType::EXT_1OPS)
4558+
.Cases("log", "log2", "log10", "log1p", FPBuiltinType::EXT_1OPS)
4559+
.Cases("sqrt", "rsqrt", "erf", "erfc", FPBuiltinType::EXT_1OPS)
4560+
.Cases("atan2", "pow", "hypot", "ldexp", FPBuiltinType::EXT_2OPS)
4561+
.Case("sincos", FPBuiltinType::EXT_3OPS)
4562+
.Default(FPBuiltinType::UNKNOWN);
4563+
return Type;
4564+
}
4565+
4566+
SPIRVValue *LLVMToSPIRVBase::transFPBuiltinIntrinsicInst(IntrinsicInst *II,
4567+
SPIRVBasicBlock *BB) {
4568+
StringRef OpName;
4569+
auto FPBuiltinTypeVal = getFPBuiltinType(II, OpName);
4570+
if (FPBuiltinTypeVal == FPBuiltinType::UNKNOWN)
4571+
return nullptr;
4572+
switch (FPBuiltinTypeVal) {
4573+
case FPBuiltinType::REGULAR_MATH: {
4574+
auto BinOp = StringSwitch<Op>(OpName)
4575+
.Case("fadd", OpFAdd)
4576+
.Case("fsub", OpFSub)
4577+
.Case("fmul", OpFMul)
4578+
.Case("fdiv", OpFDiv)
4579+
.Case("frem", OpFRem)
4580+
.Default(OpUndef);
4581+
auto *BI = BM->addBinaryInst(BinOp, transType(II->getType()),
4582+
transValue(II->getArgOperand(0), BB),
4583+
transValue(II->getArgOperand(1), BB), BB);
4584+
return addFPBuiltinDecoration(BM, II, BI);
4585+
}
4586+
case FPBuiltinType::EXT_1OPS: {
4587+
if (!checkTypeForSPIRVExtendedInstLowering(II, BM))
4588+
break;
4589+
SPIRVType *STy = transType(II->getType());
4590+
std::vector<SPIRVValue *> Ops(1, transValue(II->getArgOperand(0), BB));
4591+
auto ExtOp = StringSwitch<SPIRVWord>(OpName)
4592+
.Case("sin", OpenCLLIB::Sin)
4593+
.Case("cos", OpenCLLIB::Cos)
4594+
.Case("tan", OpenCLLIB::Tan)
4595+
.Case("sinh", OpenCLLIB::Sinh)
4596+
.Case("cosh", OpenCLLIB::Cosh)
4597+
.Case("tanh", OpenCLLIB::Tanh)
4598+
.Case("asin", OpenCLLIB::Asin)
4599+
.Case("acos", OpenCLLIB::Acos)
4600+
.Case("atan", OpenCLLIB::Atan)
4601+
.Case("asinh", OpenCLLIB::Asinh)
4602+
.Case("acosh", OpenCLLIB::Acosh)
4603+
.Case("atanh", OpenCLLIB::Atanh)
4604+
.Case("exp", OpenCLLIB::Exp)
4605+
.Case("exp2", OpenCLLIB::Exp2)
4606+
.Case("exp10", OpenCLLIB::Exp10)
4607+
.Case("expm1", OpenCLLIB::Expm1)
4608+
.Case("log", OpenCLLIB::Log)
4609+
.Case("log2", OpenCLLIB::Log2)
4610+
.Case("log10", OpenCLLIB::Log10)
4611+
.Case("log1p", OpenCLLIB::Log1p)
4612+
.Case("sqrt", OpenCLLIB::Sqrt)
4613+
.Case("rsqrt", OpenCLLIB::Rsqrt)
4614+
.Case("erf", OpenCLLIB::Erf)
4615+
.Case("erfc", OpenCLLIB::Erfc)
4616+
.Default(SPIRVWORD_MAX);
4617+
assert(ExtOp != SPIRVWORD_MAX);
4618+
auto *BI = BM->addExtInst(STy, BM->getExtInstSetId(SPIRVEIS_OpenCL), ExtOp,
4619+
Ops, BB);
4620+
return addFPBuiltinDecoration(BM, II, BI);
4621+
}
4622+
case FPBuiltinType::EXT_2OPS: {
4623+
if (!checkTypeForSPIRVExtendedInstLowering(II, BM))
4624+
break;
4625+
SPIRVType *STy = transType(II->getType());
4626+
std::vector<SPIRVValue *> Ops{transValue(II->getArgOperand(0), BB),
4627+
transValue(II->getArgOperand(1), BB)};
4628+
auto ExtOp = StringSwitch<SPIRVWord>(OpName)
4629+
.Case("atan2", OpenCLLIB::Atan2)
4630+
.Case("hypot", OpenCLLIB::Hypot)
4631+
.Case("pow", OpenCLLIB::Pow)
4632+
.Case("ldexp", OpenCLLIB::Ldexp)
4633+
.Default(SPIRVWORD_MAX);
4634+
assert(ExtOp != SPIRVWORD_MAX);
4635+
auto *BI = BM->addExtInst(STy, BM->getExtInstSetId(SPIRVEIS_OpenCL), ExtOp,
4636+
Ops, BB);
4637+
return addFPBuiltinDecoration(BM, II, BI);
4638+
}
4639+
case FPBuiltinType::EXT_3OPS: {
4640+
if (!checkTypeForSPIRVExtendedInstLowering(II, BM))
4641+
break;
4642+
SPIRVType *STy = transType(II->getType());
4643+
std::vector<SPIRVValue *> Ops{transValue(II->getArgOperand(0), BB),
4644+
transValue(II->getArgOperand(1), BB),
4645+
transValue(II->getArgOperand(2), BB)};
4646+
auto ExtOp = StringSwitch<SPIRVWord>(OpName)
4647+
.Case("sincos", OpenCLLIB::Sincos)
4648+
.Default(SPIRVWORD_MAX);
4649+
assert(ExtOp != SPIRVWORD_MAX);
4650+
auto *BI = BM->addExtInst(STy, BM->getExtInstSetId(SPIRVEIS_OpenCL), ExtOp,
4651+
Ops, BB);
4652+
return addFPBuiltinDecoration(BM, II, BI);
4653+
}
4654+
default:
4655+
return nullptr;
4656+
}
4657+
return nullptr;
4658+
}
4659+
45084660
SPIRVValue *LLVMToSPIRVBase::transFenceInst(FenceInst *FI,
45094661
SPIRVBasicBlock *BB) {
45104662
SPIRVWord MemorySemantics;

llvm-spirv/lib/SPIRV/SPIRVWriter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ class LLVMToSPIRVBase : protected BuiltinCallHelper {
108108
bool transBuiltinSet();
109109
bool isKnownIntrinsic(Intrinsic::ID Id);
110110
SPIRVValue *transIntrinsicInst(IntrinsicInst *Intrinsic, SPIRVBasicBlock *BB);
111+
enum class FPBuiltinType {
112+
REGULAR_MATH,
113+
EXT_1OPS,
114+
EXT_2OPS,
115+
EXT_3OPS,
116+
UNKNOWN
117+
};
118+
FPBuiltinType getFPBuiltinType(IntrinsicInst *II, StringRef &);
119+
SPIRVValue *transFPBuiltinIntrinsicInst(IntrinsicInst *II,
120+
SPIRVBasicBlock *BB);
111121
SPIRVValue *transFenceInst(FenceInst *FI, SPIRVBasicBlock *BB);
112122
SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB);
113123
SPIRVValue *transDirectCallInst(CallInst *Call, SPIRVBasicBlock *BB);

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVDecorate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ class SPIRVDecorate : public SPIRVDecorateGeneric {
201201
case DecorationLatencyControlLabelINTEL:
202202
case DecorationLatencyControlConstraintINTEL:
203203
return ExtensionID::SPV_INTEL_fpga_latency_control;
204+
case DecorationFPMaxErrorDecorationINTEL:
205+
return ExtensionID::SPV_INTEL_fp_max_error;
204206
case internal::DecorationCacheControlLoadINTEL:
205207
case internal::DecorationCacheControlStoreINTEL:
206208
return ExtensionID::SPV_INTEL_cache_controls;

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVEnum.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ template <> inline void SPIRVMap<Decoration, SPIRVCapVec>::init() {
501501
{CapabilityFPGALatencyControlINTEL});
502502
ADD_VEC_INIT(DecorationLatencyControlConstraintINTEL,
503503
{CapabilityFPGALatencyControlINTEL});
504+
ADD_VEC_INIT(DecorationFPMaxErrorDecorationINTEL,
505+
{CapabilityFPMaxErrorINTEL});
504506
}
505507

506508
template <> inline void SPIRVMap<BuiltIn, SPIRVCapVec>::init() {

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ template <> inline void SPIRVMap<Decoration, std::string>::init() {
211211
add(DecorationStableKernelArgumentINTEL, "StableKernelArgumentINTEL");
212212
add(DecorationLatencyControlLabelINTEL, "LatencyControlLabelINTEL");
213213
add(DecorationLatencyControlConstraintINTEL, "LatencyControlConstraintINTEL");
214+
add(DecorationFPMaxErrorDecorationINTEL, "FPMaxErrorDecorationINTEL");
214215

215216
// From spirv_internal.hpp
216217
add(internal::DecorationFuncParamKindINTEL, "FuncParamKindINTEL");
@@ -639,6 +640,7 @@ template <> inline void SPIRVMap<Capability, std::string>::init() {
639640
add(CapabilityMax, "Max");
640641
add(CapabilityFPGAArgumentInterfacesINTEL, "FPGAArgumentInterfacesINTEL");
641642
add(CapabilityFPGALatencyControlINTEL, "FPGALatencyControlINTEL");
643+
add(CapabilityFPMaxErrorINTEL, "FPMaxErrorINTEL");
642644
// From spirv_internal.hpp
643645
add(internal::CapabilityFastCompositeINTEL, "FastCompositeINTEL");
644646
add(internal::CapabilityOptNoneINTEL, "OptNoneINTEL");

llvm-spirv/spirv-headers-tag.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9b527c0fb60124936d0906d44803bec51a0200fb
1+
51b106461707f46d962554efe1bf56dee28958a3

0 commit comments

Comments
 (0)