From 8937ecf4698c9283c92c35b65e5b97e6e0acf869 Mon Sep 17 00:00:00 2001 From: Viktoria Maximova Date: Thu, 3 Feb 2022 17:53:08 +0300 Subject: [PATCH] Implement SPV_INTEL_global_variable_decorations extension (#1389) * Implement SPV_INTEL_global_variable_decorations extension This change adds support of decorations applied to global variables through `spirv.Decorations` metadata in LLVM IR. These decorations are mostly intended to help code generation for FPGA devices: * HostAccessINTEL * InitModeINTEL * ImplementInCSRINTEL Spec: https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/DeviceGlobal/SPV_INTEL_global_variable_decorations.asciidoc --- include/LLVMSPIRVExtensions.inc | 1 + lib/SPIRV/SPIRVReader.cpp | 10 +++ lib/SPIRV/SPIRVWriter.cpp | 59 +++++++++++++++ lib/SPIRV/libSPIRV/SPIRVDecorate.cpp | 10 ++- lib/SPIRV/libSPIRV/SPIRVDecorate.h | 67 +++++++++++++++++ lib/SPIRV/libSPIRV/SPIRVEnum.h | 6 ++ lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h | 5 ++ lib/SPIRV/libSPIRV/spirv_internal.hpp | 14 +++- .../global_var_decorations.ll | 73 +++++++++++++++++++ 9 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 test/transcoding/SPV_INTEL_global_variable_decorations/global_var_decorations.ll diff --git a/include/LLVMSPIRVExtensions.inc b/include/LLVMSPIRVExtensions.inc index a8f620d83b..6ed760aebf 100644 --- a/include/LLVMSPIRVExtensions.inc +++ b/include/LLVMSPIRVExtensions.inc @@ -48,3 +48,4 @@ EXT(SPV_INTEL_arithmetic_fence) EXT(SPV_INTEL_bfloat16_conversion) EXT(SPV_INTEL_joint_matrix) EXT(SPV_INTEL_hw_thread_queries) +EXT(SPV_INTEL_global_variable_decorations) diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index ce5d7bc027..812b181611 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -3599,6 +3599,16 @@ transDecorationsToMetadataList(llvm::LLVMContext *Context, OPs.push_back(LinkTypeMD); break; } + case spv::internal::DecorationHostAccessINTEL: { + const auto *const HostAccDeco = + static_cast(Deco); + auto *const AccModeMD = ConstantAsMetadata::get(ConstantInt::get( + Type::getInt32Ty(*Context), HostAccDeco->getAccessMode())); + auto *const NameMD = MDString::get(*Context, HostAccDeco->getVarName()); + OPs.push_back(AccModeMD); + OPs.push_back(NameMD); + break; + } case DecorationMergeINTEL: { const auto MergeAttrLits = Deco->getVecLiteral(); std::string FirstString = getString(MergeAttrLits); diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index 98b635832b..ef3959dff4 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -2090,6 +2090,19 @@ void addFuncPointerCallArgumentAttributes(CallInst *CI, break; \ } +void checkIsGlobalVar(SPIRVEntry *E, Decoration Dec) { + std::string ErrStr = + SPIRVDecorationNameMap::map(Dec) + " can only be applied to a variable"; + + E->getErrorLog().checkError(E->isVariable(), SPIRVEC_InvalidModule, ErrStr); + + auto AddrSpace = SPIRSPIRVAddrSpaceMap::rmap( + static_cast(E)->getStorageClass()); + ErrStr += " in a global (module) scope"; + E->getErrorLog().checkError(AddrSpace == SPIRAS_Global, SPIRVEC_InvalidModule, + ErrStr); +} + static void transMetadataDecorations(Metadata *MD, SPIRVEntry *Target) { auto *ArgDecoMD = dyn_cast(MD); assert(ArgDecoMD && "Decoration list must be a metadata node"); @@ -2151,6 +2164,52 @@ static void transMetadataDecorations(Metadata *MD, SPIRVEntry *Target) { Target, Name->getString().str(), TypeKind)); break; } + case spv::internal::DecorationHostAccessINTEL: { + checkIsGlobalVar(Target, DecoKind); + + assert(NumOperands == 3 && "HostAccessINTEL requires 2 extra operands " + "after the decoration kind number"); + auto *AccessMode = + mdconst::dyn_extract(DecoMD->getOperand(1)); + assert(AccessMode && + "HostAccessINTEL requires first extra operand to be an int"); + auto *Name = dyn_cast(DecoMD->getOperand(2)); + assert(Name && + "HostAccessINTEL requires second extra operand to be a string"); + + Target->addDecorate(new SPIRVDecorateHostAccessINTEL( + Target, AccessMode->getZExtValue(), Name->getString().str())); + break; + } + case spv::internal::DecorationInitModeINTEL: { + checkIsGlobalVar(Target, DecoKind); + assert(static_cast(Target)->getInitializer() && + "InitModeINTEL only be applied to a global (module scope) " + "variable which has an Initializer operand"); + + assert(NumOperands == 2 && + "InitModeINTEL requires exactly 1 extra operand"); + auto *Trigger = mdconst::dyn_extract(DecoMD->getOperand(1)); + assert(Trigger && + "InitModeINTEL requires extra operand to be an integer"); + + Target->addDecorate( + new SPIRVDecorateInitModeINTEL(Target, Trigger->getZExtValue())); + break; + } + case spv::internal::DecorationImplementInCSRINTEL: { + checkIsGlobalVar(Target, DecoKind); + + assert(NumOperands == 2 && + "ImplementInCSRINTEL requires exactly 1 extra operand"); + auto *Value = mdconst::dyn_extract(DecoMD->getOperand(1)); + assert(Value && + "ImplementInCSRINTEL requires extra operand to be an integer"); + + Target->addDecorate( + new SPIRVDecorateImplementInCSRINTEL(Target, Value->getZExtValue())); + break; + } default: { if (NumOperands == 1) { Target->addDecorate(new SPIRVDecorate(DecoKind, Target)); diff --git a/lib/SPIRV/libSPIRV/SPIRVDecorate.cpp b/lib/SPIRV/libSPIRV/SPIRVDecorate.cpp index 74999d9930..40017fd94e 100644 --- a/lib/SPIRV/libSPIRV/SPIRVDecorate.cpp +++ b/lib/SPIRV/libSPIRV/SPIRVDecorate.cpp @@ -101,7 +101,7 @@ size_t SPIRVDecorateGeneric::getLiteralCount() const { return Literals.size(); } void SPIRVDecorate::encode(spv_ostream &O) const { SPIRVEncoder Encoder = getEncoder(O); Encoder << Target << Dec; - switch (Dec) { + switch (static_cast(Dec)) { case DecorationLinkageAttributes: SPIRVDecorateLinkageAttr::encodeLiterals(Encoder, Literals); break; @@ -114,6 +114,9 @@ void SPIRVDecorate::encode(spv_ostream &O) const { case DecorationUserSemantic: SPIRVDecorateUserSemanticAttr::encodeLiterals(Encoder, Literals); break; + case spv::internal::DecorationHostAccessINTEL: + SPIRVDecorateHostAccessINTEL::encodeLiterals(Encoder, Literals); + break; default: Encoder << Literals; } @@ -127,7 +130,7 @@ void SPIRVDecorate::setWordCount(SPIRVWord Count) { void SPIRVDecorate::decode(std::istream &I) { SPIRVDecoder Decoder = getDecoder(I); Decoder >> Target >> Dec; - switch (Dec) { + switch (static_cast(Dec)) { case DecorationLinkageAttributes: SPIRVDecorateLinkageAttr::decodeLiterals(Decoder, Literals); break; @@ -140,6 +143,9 @@ void SPIRVDecorate::decode(std::istream &I) { case DecorationUserSemantic: SPIRVDecorateUserSemanticAttr::decodeLiterals(Decoder, Literals); break; + case spv::internal::DecorationHostAccessINTEL: + SPIRVDecorateHostAccessINTEL::decodeLiterals(Decoder, Literals); + break; default: Decoder >> Literals; } diff --git a/lib/SPIRV/libSPIRV/SPIRVDecorate.h b/lib/SPIRV/libSPIRV/SPIRVDecorate.h index eb0ef01b0e..6810295812 100644 --- a/lib/SPIRV/libSPIRV/SPIRVDecorate.h +++ b/lib/SPIRV/libSPIRV/SPIRVDecorate.h @@ -174,6 +174,10 @@ class SPIRVDecorate : public SPIRVDecorateGeneric { return ExtensionID::SPV_INTEL_fpga_invocation_pipelining_attributes; case internal::DecorationRuntimeAlignedINTEL: return ExtensionID::SPV_INTEL_runtime_aligned; + case internal::DecorationHostAccessINTEL: + case internal::DecorationInitModeINTEL: + case internal::DecorationImplementInCSRINTEL: + return ExtensionID::SPV_INTEL_global_variable_decorations; default: return {}; } @@ -698,6 +702,69 @@ class SPIRVDecoratePipelineEnableINTEL : public SPIRVDecorate { Enable){}; }; +class SPIRVDecorateHostAccessINTEL : public SPIRVDecorate { +public: + // Complete constructor for SPIRVHostAccessINTEL + SPIRVDecorateHostAccessINTEL(SPIRVEntry *TheTarget, SPIRVWord AccessMode, + const std::string &VarName) + : SPIRVDecorate(spv::internal::DecorationHostAccessINTEL, TheTarget) { + Literals.push_back(AccessMode); + for (auto &I : getVec(VarName)) + Literals.push_back(I); + WordCount += Literals.size(); + }; + + SPIRVWord getAccessMode() const { return Literals.front(); } + std::string getVarName() const { + return getString(Literals.cbegin() + 1, Literals.cend()); + } + + static void encodeLiterals(SPIRVEncoder &Encoder, + const std::vector &Literals) { +#ifdef _SPIRV_SUPPORT_TEXT_FMT + if (SPIRVUseTextFormat) { + Encoder << Literals.front(); + std::string Name = getString(Literals.cbegin() + 1, Literals.cend()); + Encoder << Name; + } else +#endif + Encoder << Literals; + } + + static void decodeLiterals(SPIRVDecoder &Decoder, + std::vector &Literals) { +#ifdef _SPIRV_SUPPORT_TEXT_FMT + if (SPIRVUseTextFormat) { + SPIRVWord Mode; + Decoder >> Mode; + std::string Name; + Decoder >> Name; + Literals.front() = Mode; + std::copy_n(getVec(Name).begin(), Literals.size() - 1, + Literals.begin() + 1); + + } else +#endif + Decoder >> Literals; + } +}; + +class SPIRVDecorateInitModeINTEL : public SPIRVDecorate { +public: + // Complete constructor for SPIRVInitModeINTEL + SPIRVDecorateInitModeINTEL(SPIRVEntry *TheTarget, SPIRVWord Trigger) + : SPIRVDecorate(spv::internal::DecorationInitModeINTEL, TheTarget, + Trigger){}; +}; + +class SPIRVDecorateImplementInCSRINTEL : public SPIRVDecorate { +public: + // Complete constructor for SPIRVImplementInCSRINTEL + SPIRVDecorateImplementInCSRINTEL(SPIRVEntry *TheTarget, SPIRVWord Value) + : SPIRVDecorate(spv::internal::DecorationImplementInCSRINTEL, TheTarget, + Value){}; +}; + } // namespace SPIRV #endif // SPIRV_LIBSPIRV_SPIRVDECORATE_H diff --git a/lib/SPIRV/libSPIRV/SPIRVEnum.h b/lib/SPIRV/libSPIRV/SPIRVEnum.h index fc67d84b0f..8ef076282b 100644 --- a/lib/SPIRV/libSPIRV/SPIRVEnum.h +++ b/lib/SPIRV/libSPIRV/SPIRVEnum.h @@ -443,6 +443,12 @@ template <> inline void SPIRVMap::init() { {internal::CapabilityFPGAInvocationPipeliningAttributesINTEL}); ADD_VEC_INIT(internal::DecorationRuntimeAlignedINTEL, {internal::CapabilityRuntimeAlignedAttributeINTEL}); + ADD_VEC_INIT(internal::DecorationHostAccessINTEL, + {internal::CapabilityGlobalVariableDecorationsINTEL}); + ADD_VEC_INIT(internal::DecorationInitModeINTEL, + {internal::CapabilityGlobalVariableDecorationsINTEL}); + ADD_VEC_INIT(internal::DecorationImplementInCSRINTEL, + {internal::CapabilityGlobalVariableDecorationsINTEL}); ADD_VEC_INIT(internal::DecorationArgumentAttributeINTEL, {CapabilityFunctionPointersINTEL}); } diff --git a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h index 733e0be411..3346293bb2 100644 --- a/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h +++ b/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h @@ -180,6 +180,9 @@ template <> inline void SPIRVMap::init() { add(internal::DecorationMaxConcurrencyINTEL, "MaxConcurrencyINTEL"); add(internal::DecorationPipelineEnableINTEL, "PipelineEnableINTEL"); add(internal::DecorationRuntimeAlignedINTEL, "RuntimeAlignedINTEL"); + add(internal::DecorationHostAccessINTEL, "HostAccessINTEL"); + add(internal::DecorationInitModeINTEL, "InitModeINTEL"); + add(internal::DecorationImplementInCSRINTEL, "ImplementInCSRINTEL"); add(internal::DecorationArgumentAttributeINTEL, "ArgumentAttributeINTEL"); add(DecorationMax, "Max"); @@ -585,6 +588,8 @@ template <> inline void SPIRVMap::init() { add(internal::CapabilityBfloat16ConversionINTEL, "Bfloat16ConversionINTEL"); add(internal::CapabilityJointMatrixINTEL, "JointMatrixINTEL"); add(internal::CapabilityHWThreadQueryINTEL, "HWThreadQueryINTEL"); + add(internal::CapabilityGlobalVariableDecorationsINTEL, + "GlobalVariableDecorationsINTEL"); } SPIRV_DEF_NAMEMAP(Capability, SPIRVCapabilityNameMap) diff --git a/lib/SPIRV/libSPIRV/spirv_internal.hpp b/lib/SPIRV/libSPIRV/spirv_internal.hpp index f01b7763c4..88f33daa9e 100644 --- a/lib/SPIRV/libSPIRV/spirv_internal.hpp +++ b/lib/SPIRV/libSPIRV/spirv_internal.hpp @@ -60,6 +60,9 @@ enum InternalDecoration { IDecPipelineEnableINTEL = 5919, IDecRuntimeAlignedINTEL = 5940, IDecCallableFunctionINTEL = 6087, + IDecHostAccessINTEL = 6147, + IDecInitModeINTEL = 6148, + IDecImplementInCSRINTEL = 6149, IDecArgumentAttributeINTEL = 6409 }; @@ -74,7 +77,8 @@ enum InternalCapability { ICapBfloat16ConversionINTEL = 6115, ICapabilityJointMatrixINTEL = 6118, ICapabilityHWThreadQueryINTEL = 6134, - ICapFPArithmeticFenceINTEL = 6144 + ICapFPArithmeticFenceINTEL = 6144, + ICapGlobalVariableDecorationsINTEL = 6146 }; enum InternalFunctionControlMask { IFunctionControlOptNoneINTELMask = 0x10000 }; @@ -137,6 +141,12 @@ constexpr Decoration DecorationCallableFunctionINTEL = static_cast(IDecCallableFunctionINTEL); constexpr Decoration DecorationRuntimeAlignedINTEL = static_cast(IDecRuntimeAlignedINTEL); +constexpr Decoration DecorationHostAccessINTEL = + static_cast(IDecHostAccessINTEL); +constexpr Decoration DecorationInitModeINTEL = + static_cast(IDecInitModeINTEL); +constexpr Decoration DecorationImplementInCSRINTEL = + static_cast(IDecImplementInCSRINTEL); constexpr Decoration DecorationArgumentAttributeINTEL = static_cast(IDecArgumentAttributeINTEL); @@ -158,6 +168,8 @@ constexpr Capability CapabilityFPArithmeticFenceINTEL = static_cast(ICapFPArithmeticFenceINTEL); constexpr Capability CapabilityBfloat16ConversionINTEL = static_cast(ICapBfloat16ConversionINTEL); +constexpr Capability CapabilityGlobalVariableDecorationsINTEL = + static_cast(ICapGlobalVariableDecorationsINTEL); constexpr FunctionControlMask FunctionControlOptNoneINTELMask = static_cast(IFunctionControlOptNoneINTELMask); diff --git a/test/transcoding/SPV_INTEL_global_variable_decorations/global_var_decorations.ll b/test/transcoding/SPV_INTEL_global_variable_decorations/global_var_decorations.ll new file mode 100644 index 0000000000..993112cc2f --- /dev/null +++ b/test/transcoding/SPV_INTEL_global_variable_decorations/global_var_decorations.ll @@ -0,0 +1,73 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-spirv %t.bc --spirv-ext=+SPV_INTEL_global_variable_decorations -o %t.spv +; RUN: llvm-spirv %t.spv -to-text -o %t.spt +; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV + +; RUN: llvm-spirv -r %t.spv --spirv-target-env=SPV-IR -o %t.rev.bc +; RUN: llvm-dis %t.rev.bc +; RUN: FileCheck < %t.rev.ll %s --check-prefix=CHECK-SPV-IR + +; RUN: llvm-spirv -r %t.spv -o %t.rev.bc +; RUN: llvm-dis %t.rev.bc +; RUN: FileCheck < %t.rev.ll %s --check-prefix=CHECK-LLVM + +; Expected to fail - the decorations require enabled extension to be translated. +; RUN: not llvm-spirv %t.bc -o %t.spv + +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +@int_var = addrspace(1) global i32 42, !spirv.Decorations !1 +@float_var = addrspace(1) global float 1.0, !spirv.Decorations !6 +@bool_var = addrspace(1) global i1 0, !spirv.Decorations !9 + +; CHECK-SPIRV: Capability GlobalVariableDecorationsINTEL +; CHECK-SPIRV: Extension "SPV_INTEL_global_variable_decorations" +; CHECK-SPIRV: Decorate [[#INT_VAR_ID:]] HostAccessINTEL 1 "IntVarName" +; CHECK-SPIRV: Decorate [[#INT_VAR_ID]] ImplementInCSRINTEL 1 +; CHECK-SPIRV: Decorate [[#INT_VAR_ID]] InitModeINTEL 0 + +; CHECK-SPIRV: Decorate [[#FLOAT_VAR_ID:]] ImplementInCSRINTEL 1 +; CHECK-SPIRV: Decorate [[#FLOAT_VAR_ID]] InitModeINTEL 1 + +; CHECK-SPIRV: Decorate [[#BOOL_VAR_ID:]] HostAccessINTEL 3 "BoolVarName" +; CHECK-SPIRV: Decorate [[#BOOL_VAR_ID]] ImplementInCSRINTEL 0 +; CHECK-SPIRV: Decorate [[#BOOL_VAR_ID]] InitModeINTEL 0 + +; 5 is a global storage +; CHECK-SPIRV: Variable [[#]] [[#INT_VAR_ID]] 5 +; CHECK-SPIRV: Variable [[#]] [[#FLOAT_VAR_ID]] 5 +; CHECK-SPIRV: Variable [[#]] [[#BOOL_VAR_ID]] 5 + +!1 = !{!2, !3, !4} +!2 = !{i32 6147, i32 1, !"IntVarName"} ; HostAccessINTEL 1 "IntVarName" +!3 = !{i32 6149, i1 true} ; ImplementInCSRINTEL = true +!4 = !{i32 6148, i32 0} ; InitModeINTEL = 0 +!5 = !{i32 6148, i32 1} ; InitModeINTEL = 1 +!6 = !{!3, !5} +!7 = !{i32 6147, i32 3, !"BoolVarName"} ; HostAccessINTEL 3 "BoolVarName" +!8 = !{i32 6149, i1 false} ; ImplementInCSRINTEL = false +!9 = !{!7, !8, !4} + +; CHECK-SPV-IR: @int_var = addrspace(1) global i32 42, !spirv.Decorations ![[#INT_VAR_DEC:]] +; CHECK-SPV-IR: @float_var = addrspace(1) global float 1.000000e+00, !spirv.Decorations ![[#FLOAT_VAR_DEC:]] +; CHECK-SPV-IR: @bool_var = addrspace(1) global i1 false, !spirv.Decorations ![[#BOOL_VAR_DEC:]] + +; CHECK-SPV-IR: ![[#INT_VAR_DEC]] = !{![[#]], ![[#MD_HOST_ACCESS_INTVAR:]], ![[#MD_INIT_0:]], ![[#MD_CSR_1:]]} +; CHECK-SPV-IR: ![[#MD_HOST_ACCESS_INTVAR]] = !{i32 6147, i32 1, !"IntVarName"} +; CHECK-SPV-IR: ![[#MD_INIT_0]] = !{i32 6148, i32 0} +; CHECK-SPV-IR: ![[#MD_CSR_1]] = !{i32 6149, i32 1} +; CHECK-SPV-IR: ![[#FLOAT_VAR_DEC]] = !{![[#]], ![[#MD_INIT_1:]], ![[#MD_CSR_1]]} +; CHECK-SPV-IR: ![[#MD_INIT_1]] = !{i32 6148, i32 1} +; CHECK-SPV-IR: ![[#BOOL_VAR_DEC]] = !{![[#]], ![[#MD_HOST_ACCESS_BOOLVAR:]], ![[#MD_INIT_0]], ![[#MD_CSR_0:]]} +; CHECK-SPV-IR: ![[#MD_HOST_ACCESS_BOOLVAR]] = !{i32 6147, i32 3, !"BoolVarName"} +; CHECK-SPV-IR: ![[#MD_CSR_0]] = !{i32 6149, i32 0} + + +; CHECK-LLVM-NOT: @int_var = {{.*}}, !spirv.Decorations ![[#]] +; CHECK-LLVM-NOT: @float_var = {{.*}}, !spirv.Decorations ![[#]] +; CHECK-LLVM-NOT: @bool_var = {{.*}}, !spirv.Decorations ![[#]] + +; CHECK-LLVM: @int_var = addrspace(1) global i32 42 +; CHECK-LLVM: @float_var = addrspace(1) global float 1.000000e+00 +; CHECK-LLVM: @bool_var = addrspace(1) global i1 false