From d7bb9ee170e3450b34f0d98e116e251c82ffa889 Mon Sep 17 00:00:00 2001 From: Ehsan Date: Tue, 1 Dec 2020 14:04:14 -0600 Subject: [PATCH] Introduce the implicit 'vk' namespace (#3133) * [spirv] Introduce the implicit 'vk' namespace If any of these are used for DXIL code generation, the compiler will report an error about the unknown "vk" namespace. * [spirv] Introduce vk::ReadClock intrinsic. The following Vulkan specific intrinsic functions are added: ```hlsl uint64_t vk::ReadClock(in uint32 scope); ``` Also the following Vulkan-specific implicit constants are added: ``` vk::CrossDeviceScope; // defined as uint 0 vk::DeviceScope // defined as uint 1 vk::WorkgroupScope // defined as uint 2 vk::SubgroupScope // defined as uint 3 vk::InvocationScope // defined as uint 4 vk::QueueFamilyScope // defined as uint 5 ``` Sample usage looks as follows: ```hlsl uint64_t clock = vk::ReadClock(vk::WorkgroupScope); ``` If any of these are used for DXIL code generation, the compiler will report an error about the unknown "vk" namespace. * [spirv] Add documentation. * Address code review comments. * Test: Validate vk namespace is not allowed for dxil. * Fix usage of DXASSERT. * Move ValidateVkNamespaceNotAllowed test to HlslFileCheck. --- docs/SPIR-V.rst | 73 ++++++++++++ include/dxc/HlslIntrinsicOp.h | 3 + lib/HLSL/HLOperationLower.cpp | 3 + .../include/clang/SPIRV/FeatureManager.h | 1 + .../clang/include/clang/SPIRV/SpirvBuilder.h | 2 + .../include/clang/SPIRV/SpirvInstruction.h | 20 ++++ .../clang/include/clang/SPIRV/SpirvVisitor.h | 2 + tools/clang/lib/SPIRV/CapabilityVisitor.cpp | 8 ++ tools/clang/lib/SPIRV/CapabilityVisitor.h | 1 + tools/clang/lib/SPIRV/EmitVisitor.cpp | 11 ++ tools/clang/lib/SPIRV/EmitVisitor.h | 1 + tools/clang/lib/SPIRV/FeatureManager.cpp | 3 + tools/clang/lib/SPIRV/SpirvBuilder.cpp | 10 ++ tools/clang/lib/SPIRV/SpirvEmitter.cpp | 36 +++++- tools/clang/lib/SPIRV/SpirvEmitter.h | 3 + tools/clang/lib/SPIRV/SpirvInstruction.cpp | 6 + tools/clang/lib/Sema/SemaExpr.cpp | 13 ++- tools/clang/lib/Sema/SemaHLSL.cpp | 110 +++++++++++++++++- tools/clang/lib/Sema/SemaOverload.cpp | 13 ++- tools/clang/lib/Sema/VkConstantsTables.h | 29 +++++ .../lib/Sema/gen_intrin_main_tables_15.h | 20 ++++ .../intrinsics.vkcrossdevicescope.hlsl | 21 ++++ .../intrinsics.vkdevicescope.hlsl | 21 ++++ .../intrinsics.vkinvocationscope.hlsl | 21 ++++ .../intrinsics.vkqueuefamilyscope.hlsl | 21 ++++ .../CodeGenSPIRV/intrinsics.vkreadclock.hlsl | 24 ++++ .../intrinsics.vksubgroupscope.hlsl | 21 ++++ .../intrinsics.vkworkgroupscope.hlsl | 21 ++++ .../hlsl/namespace/vk-namespace.hlsl | 21 ++++ .../unittests/SPIRV/CodeGenSpirvTest.cpp | 23 ++++ utils/hct/gen_intrin_main.txt | 9 ++ utils/hct/hctdb.py | 6 + utils/hct/hctdb_instrhelp.py | 2 +- 33 files changed, 567 insertions(+), 12 deletions(-) create mode 100644 tools/clang/lib/Sema/VkConstantsTables.h create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkcrossdevicescope.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkdevicescope.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkinvocationscope.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkqueuefamilyscope.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkreadclock.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vksubgroupscope.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.vkworkgroupscope.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/namespace/vk-namespace.hlsl diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index f5211187d..fd07db8aa 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -3552,6 +3552,79 @@ Quad ``QuadReadAcrossDiagonal()`` ``OpGroupNonUniformQuadSwap`` Quad ``QuadReadLaneAt()`` ``OpGroupNonUniformQuadBroadcast`` ============= ============================ =================================== ====================== +The Implicit ``vk`` Namespace +============================= + +Overview +-------- +We have introduced an implicit namepace (called ``vk``) that will be home to all +Vulkan-specific functions, enums, etc. Given the similarity between HLSL and +C++, developers are likely familiar with namespaces -- and implicit namespaces +(e.g. ``std::`` in C++). The ``vk`` namespace provides an interface for expressing +Vulkan-specific features (core spec and KHR extensions). + +**The compiler will generate the proper error message (** ``unknown 'vk' identifier`` **) +if** ``vk::`` **is used for compiling to DXIL.** + +Any intrinsic function or enum in the vk namespace will be deprecated if an +equivalent one is added to the default namepsace. + +Current Features +---------------- +The following intrinsic functions and constants are currently defined in the +implicit ``vk`` namepsace. + +.. code:: hlsl + + // Implicitly defined when compiling to SPIR-V. + namespace vk { + + const uint CrossDeviceScope = 0; + const uint DeviceScope = 1; + const uint WorkgroupScope = 2; + const uint SubgroupScope = 3; + const uint InvocationScope = 4; + const uint QueueFamilyScope = 5; + + uint64_t ReadClock(in uint scope); + } // end namespace + + +Intrinsic Constants +------------------- +The following constants are currently defined: + +======================== ============================================ + Constant value (SPIR-V constant equivalent, if any) +======================== ============================================ +``vk::CrossDeviceScope`` ``0`` (``CrossDevice``) +``vk::DeviceScope`` ``1`` (``Device``) +``vk::WorkgroupScope`` ``2`` (``Workgroup``) +``vk::SubgroupScope`` ``3`` (``Subgroup``) +``vk::InvocationScope`` ``4`` (``Invocation``) +``vk::QueueFamilyScope`` ``5`` (``QueueFamily``) +======================== ============================================ + +Intrinsic Functions +------------------- + +ReadClock +~~~~~~~~~ +This intrinsic funcion has the following signature: + +.. code:: hlsl + + uint64_t ReadClock(in uint scope); + +It translates to performing ``OpReadClockKHR`` defined in `VK_KHR_shader_clock `_. +One can use the predefined scopes in the ``vk`` namepsace to specify the scope argument. +For example: + +.. code:: hlsl + + uint64_t clock = vk::ReadClock(vk::SubgroupScope); + + Supported Command-line Options ============================== diff --git a/include/dxc/HlslIntrinsicOp.h b/include/dxc/HlslIntrinsicOp.h index 8285b394d..292bb1af4 100644 --- a/include/dxc/HlslIntrinsicOp.h +++ b/include/dxc/HlslIntrinsicOp.h @@ -224,6 +224,9 @@ import hctdb_instrhelp IOP_texCUBEproj, IOP_transpose, IOP_trunc, +#ifdef ENABLE_SPIRV_CODEGEN + IOP_VkReadClock, +#endif // ENABLE_SPIRV_CODEGEN MOP_Append, MOP_RestartStrip, MOP_CalculateLevelOfDetail, diff --git a/lib/HLSL/HLOperationLower.cpp b/lib/HLSL/HLOperationLower.cpp index 583087519..07b4ff0b3 100644 --- a/lib/HLSL/HLOperationLower.cpp +++ b/lib/HLSL/HLOperationLower.cpp @@ -5355,6 +5355,9 @@ IntrinsicLower gLowerTable[] = { {IntrinsicOp::IOP_texCUBEproj, EmptyLower, DXIL::OpCode::NumOpCodes}, {IntrinsicOp::IOP_transpose, EmptyLower, DXIL::OpCode::NumOpCodes}, {IntrinsicOp::IOP_trunc, TrivialUnaryOperation, DXIL::OpCode::Round_z}, + // SPIR-V Change Starts + {IntrinsicOp::IOP_VkReadClock, UnsupportedVulkanIntrinsic, DXIL::OpCode::NumOpCodes}, + // SPIR-V Change Ends {IntrinsicOp::MOP_Append, StreamOutputLower, DXIL::OpCode::EmitStream}, {IntrinsicOp::MOP_RestartStrip, StreamOutputLower, DXIL::OpCode::CutStream}, diff --git a/tools/clang/include/clang/SPIRV/FeatureManager.h b/tools/clang/include/clang/SPIRV/FeatureManager.h index ac4a2410d..4f2773793 100644 --- a/tools/clang/include/clang/SPIRV/FeatureManager.h +++ b/tools/clang/include/clang/SPIRV/FeatureManager.h @@ -36,6 +36,7 @@ enum class Extension { KHR_shader_draw_parameters, KHR_post_depth_coverage, KHR_ray_tracing, + KHR_shader_clock, EXT_demote_to_helper_invocation, EXT_descriptor_indexing, EXT_fragment_fully_covered, diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index 70165617c..e132d194f 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -496,6 +496,8 @@ public: createRayQueryOpsKHR(spv::Op opcode, QualType resultType, llvm::ArrayRef operands, bool cullFlags, SourceLocation loc); + /// \brief Creates an OpReadClockKHR instruction. + SpirvInstruction *createReadClock(SpirvInstruction *scope, SourceLocation); // === SPIR-V Module Structure === inline void setMemoryModel(spv::AddressingModel, spv::MemoryModel); diff --git a/tools/clang/include/clang/SPIRV/SpirvInstruction.h b/tools/clang/include/clang/SPIRV/SpirvInstruction.h index 5bd697eae..b14a56e2d 100644 --- a/tools/clang/include/clang/SPIRV/SpirvInstruction.h +++ b/tools/clang/include/clang/SPIRV/SpirvInstruction.h @@ -118,6 +118,7 @@ public: IK_Load, // OpLoad IK_RayQueryOpKHR, // KHR rayquery ops IK_RayTracingOpNV, // NV raytracing ops + IK_ReadClock, // OpReadClock IK_SampledImage, // OpSampledImage IK_Select, // OpSelect IK_SpecConstantBinaryOp, // SpecConstant binary operations @@ -2702,6 +2703,25 @@ private: SpirvDebugInfoNone *debugNone; }; +class SpirvReadClock : public SpirvInstruction { +public: + SpirvReadClock(QualType resultType, SpirvInstruction *scope, SourceLocation); + + DEFINE_RELEASE_MEMORY_FOR_CLASS(SpirvReadClock) + + // For LLVM-style RTTI + static bool classof(const SpirvInstruction *inst) { + return inst->getKind() == IK_ReadClock; + } + + bool invokeVisitor(Visitor *v) override; + + SpirvInstruction *getScope() const { return scope; } + +private: + SpirvInstruction *scope; +}; + #undef DECLARE_INVOKE_VISITOR_FOR_CLASS } // namespace spirv diff --git a/tools/clang/include/clang/SPIRV/SpirvVisitor.h b/tools/clang/include/clang/SPIRV/SpirvVisitor.h index f8a15b877..d425e0827 100644 --- a/tools/clang/include/clang/SPIRV/SpirvVisitor.h +++ b/tools/clang/include/clang/SPIRV/SpirvVisitor.h @@ -137,6 +137,8 @@ public: DEFINE_VISIT_METHOD(SpirvDebugTypeTemplateParameter) DEFINE_VISIT_METHOD(SpirvRayQueryOpKHR) + DEFINE_VISIT_METHOD(SpirvReadClock) + #undef DEFINE_VISIT_METHOD protected: diff --git a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp index ae81a4eda..755b59c62 100644 --- a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp +++ b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp @@ -605,6 +605,14 @@ bool CapabilityVisitor::visit(SpirvDemoteToHelperInvocationEXT *inst) { return true; } +bool CapabilityVisitor::visit(SpirvReadClock *inst) { + auto loc = inst->getSourceLocation(); + addCapabilityForType(inst->getResultType(), loc, inst->getStorageClass()); + addCapability(spv::Capability::ShaderClockKHR, loc); + addExtension(Extension::KHR_shader_clock, "ReadClock", loc); + return true; +} + bool CapabilityVisitor::visit(SpirvModule *, Visitor::Phase phase) { // If there are no entry-points in the module (hence shaderModel is not set), // add the Linkage capability. This allows library shader models to use diff --git a/tools/clang/lib/SPIRV/CapabilityVisitor.h b/tools/clang/lib/SPIRV/CapabilityVisitor.h index b1969c80a..c50669bdb 100644 --- a/tools/clang/lib/SPIRV/CapabilityVisitor.h +++ b/tools/clang/lib/SPIRV/CapabilityVisitor.h @@ -38,6 +38,7 @@ public: bool visit(SpirvExtInstImport *) override; bool visit(SpirvExtInst *) override; bool visit(SpirvDemoteToHelperInvocationEXT *) override; + bool visit(SpirvReadClock *) override; using Visitor::visit; diff --git a/tools/clang/lib/SPIRV/EmitVisitor.cpp b/tools/clang/lib/SPIRV/EmitVisitor.cpp index a5ebb06f8..4966334ed 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.cpp +++ b/tools/clang/lib/SPIRV/EmitVisitor.cpp @@ -1634,6 +1634,17 @@ bool EmitVisitor::visit(SpirvRayQueryOpKHR *inst) { return true; } +bool EmitVisitor::visit(SpirvReadClock *inst) { + initInstruction(inst); + curInst.push_back(inst->getResultTypeId()); + curInst.push_back(getOrAssignResultId(inst)); + curInst.push_back(getOrAssignResultId(inst->getScope())); + finalizeInstruction(&mainBinary); + emitDebugNameForInstruction(getOrAssignResultId(inst), + inst->getDebugName()); + return true; +} + // EmitTypeHandler ------ void EmitTypeHandler::initTypeInstruction(spv::Op op) { diff --git a/tools/clang/lib/SPIRV/EmitVisitor.h b/tools/clang/lib/SPIRV/EmitVisitor.h index d8b140c54..61748cc86 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.h +++ b/tools/clang/lib/SPIRV/EmitVisitor.h @@ -266,6 +266,7 @@ public: bool visit(SpirvRayTracingOpNV *) override; bool visit(SpirvDemoteToHelperInvocationEXT *) override; bool visit(SpirvRayQueryOpKHR *) override; + bool visit(SpirvReadClock *) override; bool visit(SpirvDebugInfoNone *) override; bool visit(SpirvDebugSource *) override; diff --git a/tools/clang/lib/SPIRV/FeatureManager.cpp b/tools/clang/lib/SPIRV/FeatureManager.cpp index 58def3553..c2d449078 100644 --- a/tools/clang/lib/SPIRV/FeatureManager.cpp +++ b/tools/clang/lib/SPIRV/FeatureManager.cpp @@ -136,6 +136,7 @@ Extension FeatureManager::getExtensionSymbol(llvm::StringRef name) { Extension::GOOGLE_hlsl_functionality1) .Case("SPV_GOOGLE_user_type", Extension::GOOGLE_user_type) .Case("SPV_KHR_post_depth_coverage", Extension::KHR_post_depth_coverage) + .Case("SPV_KHR_shader_clock", Extension::KHR_shader_clock) .Case("SPV_NV_ray_tracing", Extension::NV_ray_tracing) .Case("SPV_NV_mesh_shader", Extension::NV_mesh_shader) .Case("SPV_KHR_ray_query", Extension::KHR_ray_query) @@ -160,6 +161,8 @@ const char *FeatureManager::getExtensionName(Extension symbol) { return "SPV_KHR_post_depth_coverage"; case Extension::KHR_ray_tracing: return "SPV_KHR_ray_tracing"; + case Extension::KHR_shader_clock: + return "SPV_KHR_shader_clock"; case Extension::EXT_demote_to_helper_invocation: return "SPV_EXT_demote_to_helper_invocation"; case Extension::EXT_descriptor_indexing: diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index 822dd3034..93317c020 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -929,6 +929,16 @@ SpirvBuilder::createRayQueryOpsKHR(spv::Op opcode, QualType resultType, return inst; } +SpirvInstruction *SpirvBuilder::createReadClock(SpirvInstruction *scope, + SourceLocation loc) { + assert(insertPoint && "null insert point"); + assert(scope->getAstResultType()->isIntegerType()); + auto *inst = + new (context) SpirvReadClock(astContext.UnsignedLongLongTy, scope, loc); + insertPoint->addInstruction(inst); + return inst; +} + void SpirvBuilder::addModuleProcessed(llvm::StringRef process) { mod->addModuleProcessed(new (context) SpirvModuleProcessed({}, process)); } diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index efbde544d..3aeb4a69c 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -38,6 +38,24 @@ namespace spirv { namespace { +// Returns true if the given decl is an implicit variable declaration inside the +// "vk" namespace. +bool isImplicitVarDeclInVkNamespace(const Decl *decl) { + if (!decl) + return false; + + if (auto *varDecl = dyn_cast(decl)) { + // Check whether it is implicitly defined. + if (!decl->isImplicit()) + return false; + + if (auto *nsDecl = dyn_cast(varDecl->getDeclContext())) + if (nsDecl->getName().equals("vk")) + return true; + } + return false; +} + // Returns true if the given decl has the given semantic. bool hasSemantic(const DeclaratorDecl *decl, hlsl::DXIL::SemanticKind semanticKind) { @@ -809,8 +827,12 @@ SpirvInstruction *SpirvEmitter::doExpr(const Expr *expr) { expr = expr->IgnoreParens(); if (const auto *declRefExpr = dyn_cast(expr)) { - result = declIdMapper.getDeclEvalInfo(declRefExpr->getDecl(), - expr->getLocStart()); + auto *decl = declRefExpr->getDecl(); + if (isImplicitVarDeclInVkNamespace(declRefExpr->getDecl())) { + result = doExpr(cast(decl)->getInit()); + } else { + result = declIdMapper.getDeclEvalInfo(decl, expr->getLocStart()); + } } else if (const auto *memberExpr = dyn_cast(expr)) { result = doMemberExpr(memberExpr); } else if (const auto *castExpr = dyn_cast(expr)) { @@ -7312,6 +7334,9 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { case hlsl::IntrinsicOp::IOP_rcp: retVal = processIntrinsicRcp(callExpr); break; + case hlsl::IntrinsicOp::IOP_VkReadClock: + retVal = processIntrinsicReadClock(callExpr); + break; case hlsl::IntrinsicOp::IOP_saturate: retVal = processIntrinsicSaturate(callExpr); break; @@ -9199,6 +9224,13 @@ SpirvInstruction *SpirvEmitter::processIntrinsicRcp(const CallExpr *callExpr) { getValueOne(argType), argId, loc); } +SpirvInstruction * +SpirvEmitter::processIntrinsicReadClock(const CallExpr *callExpr) { + auto *scope = doExpr(callExpr->getArg(0)); + assert(scope->getAstResultType()->isIntegerType()); + return spvBuilder.createReadClock(scope, callExpr->getExprLoc()); +} + SpirvInstruction * SpirvEmitter::processIntrinsicAllOrAny(const CallExpr *callExpr, spv::Op spvOp) { diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index ab6b596df..6878513ec 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -488,6 +488,9 @@ private: /// Processes the 'rcp' intrinsic function. SpirvInstruction *processIntrinsicRcp(const CallExpr *); + /// Processes the 'ReadClock' intrinsic function. + SpirvInstruction *processIntrinsicReadClock(const CallExpr *); + /// Processes the 'sign' intrinsic function for float types. /// The FSign instruction in the GLSL instruction set returns a floating point /// result. The HLSL sign function, however, returns an integer. An extra diff --git a/tools/clang/lib/SPIRV/SpirvInstruction.cpp b/tools/clang/lib/SPIRV/SpirvInstruction.cpp index 5e811d5ac..400110aea 100644 --- a/tools/clang/lib/SPIRV/SpirvInstruction.cpp +++ b/tools/clang/lib/SPIRV/SpirvInstruction.cpp @@ -104,6 +104,7 @@ DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvDebugTypeMember) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvDebugTypeTemplate) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvDebugTypeTemplateParameter) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvRayQueryOpKHR) +DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvReadClock) #undef DEFINE_INVOKE_VISITOR_FOR_CLASS @@ -981,5 +982,10 @@ SpirvRayQueryOpKHR::SpirvRayQueryOpKHR( : SpirvInstruction(IK_RayQueryOpKHR, opcode, resultType, loc), operands(vecOperands.begin(), vecOperands.end()), cullFlags(flags) {} +SpirvReadClock::SpirvReadClock(QualType resultType, SpirvInstruction *s, + SourceLocation loc) + : SpirvInstruction(IK_ReadClock, spv::Op::OpReadClockKHR, resultType, loc), + scope(s) {} + } // namespace spirv } // namespace clang diff --git a/tools/clang/lib/Sema/SemaExpr.cpp b/tools/clang/lib/Sema/SemaExpr.cpp index 0736da5be..e49e0312a 100644 --- a/tools/clang/lib/Sema/SemaExpr.cpp +++ b/tools/clang/lib/Sema/SemaExpr.cpp @@ -2739,8 +2739,17 @@ bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS, return false; // Never if a scope specifier was provided. - if (SS.isSet()) - return false; + if (SS.isSet()) { + // HLSL Change begins + // We want to be able to have intrinsics inside the "vk" namespace. + const bool isVkNamespace = + SS.getScopeRep() && SS.getScopeRep()->getAsNamespace() && + SS.getScopeRep()->getAsNamespace()->getName() == "vk"; + + if (!isVkNamespace) + // HLSL Change ends + return false; + } // Only in C++ or ObjC++. if (!getLangOpts().CPlusPlus) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 86d0adbf7..7e4b09fa1 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -36,6 +36,7 @@ #include "dxc/dxcapi.internal.h" #include "dxc/HlslIntrinsicOp.h" #include "gen_intrin_main_tables_15.h" +#include "VkConstantsTables.h" #include "dxc/HLSL/HLOperations.h" #include "dxc/DXIL/DxilShaderModel.h" #include @@ -2914,8 +2915,12 @@ private: // Declaration for matrix and vector templates. ClassTemplateDecl* m_matrixTemplateDecl; ClassTemplateDecl* m_vectorTemplateDecl; - // Namespace decl for hlsl intrin functions + // Namespace decl for hlsl intrinsic functions NamespaceDecl* m_hlslNSDecl; + + // Namespace decl for Vulkan-specific intrinsic functions + NamespaceDecl* m_vkNSDecl; + // Context being processed. _Notnull_ ASTContext* m_context; @@ -3340,6 +3345,61 @@ private: return -1; } + // Adds intrinsic function declarations to the "vk" namespace. + // It does so only if SPIR-V code generation is being done. + // Assumes the implicit "vk" namespace has already been created. + void AddVkIntrinsicFunctions() { + // If not doing SPIR-V CodeGen, return. + if (!m_sema->getLangOpts().SPIRV) + return; + + DXASSERT(m_vkNSDecl, "caller has not created the vk namespace yet"); + + auto &context = m_sema->getASTContext(); + for (uint32_t i = 0; i < _countof(g_VkIntrinsics); ++i) { + const HLSL_INTRINSIC *intrinsic = &g_VkIntrinsics[i]; + const IdentifierInfo &fnII = context.Idents.get( + intrinsic->pArgs->pName, tok::TokenKind::identifier); + DeclarationName functionName(&fnII); + FunctionDecl *functionDecl = FunctionDecl::Create( + context, m_vkNSDecl, NoLoc, DeclarationNameInfo(functionName, NoLoc), + /*functionType*/ {}, nullptr, StorageClass::SC_Extern, + InlineSpecifiedFalse, HasWrittenPrototypeTrue); + m_vkNSDecl->addDecl(functionDecl); + functionDecl->setLexicalDeclContext(m_vkNSDecl); + functionDecl->setDeclContext(m_vkNSDecl); + functionDecl->setImplicit(true); + } + } + + // Adds implicitly defined Vulkan-specific constants to the "vk" namespace. + // It does so only if SPIR-V code generation is being done. + // Assumes the implicit "vk" namespace has already been created. + void AddVkIntrinsicConstants() { + // If not doing SPIR-V CodeGen, return. + if (!m_sema->getLangOpts().SPIRV) + return; + + DXASSERT(m_vkNSDecl, "caller has not created the vk namespace yet"); + + for (auto intConst : GetVkIntegerConstants()) { + const llvm::StringRef name = intConst.first; + const uint32_t value = intConst.second; + auto &context = m_sema->getASTContext(); + QualType type = context.getConstType(context.UnsignedIntTy); + IdentifierInfo &Id = context.Idents.get(name, tok::TokenKind::identifier); + VarDecl *varDecl = + VarDecl::Create(context, m_vkNSDecl, NoLoc, NoLoc, &Id, type, + context.getTrivialTypeSourceInfo(type), + clang::StorageClass::SC_Static); + Expr *exprVal = IntegerLiteral::Create( + context, llvm::APInt(context.getIntWidth(type), value), type, NoLoc); + varDecl->setInit(exprVal); + varDecl->setImplicit(true); + m_vkNSDecl->addDecl(varDecl); + } + } + // Adds all built-in HLSL object types. void AddObjectTypes() { @@ -3504,6 +3564,8 @@ public: HLSLExternalSource() : m_matrixTemplateDecl(nullptr), m_vectorTemplateDecl(nullptr), + m_hlslNSDecl(nullptr), + m_vkNSDecl(nullptr), m_context(nullptr), m_sema(nullptr), m_hlslStringTypedef(nullptr) @@ -3532,14 +3594,29 @@ public: void InitializeSema(Sema& S) override { + auto &context = S.getASTContext(); m_sema = &S; S.addExternalSource(this); AddObjectTypes(); - AddStdIsEqualImplementation(S.getASTContext(), S); + AddStdIsEqualImplementation(context, S); for (auto && intrinsic : m_intrinsicTables) { AddIntrinsicTableMethods(intrinsic); } + + if (m_sema->getLangOpts().SPIRV) { + // Create the "vk" namespace which contains Vulkan-specific intrinsics. + m_vkNSDecl = + NamespaceDecl::Create(context, context.getTranslationUnitDecl(), + /*Inline*/ false, SourceLocation(), + SourceLocation(), &context.Idents.get("vk"), + /*PrevDecl*/ nullptr); + context.getTranslationUnitDecl()->addDecl(m_vkNSDecl); + + // Add Vulkan-specific intrinsics. + AddVkIntrinsicFunctions(); + AddVkIntrinsicConstants(); + } } void ForgetSema() override @@ -4271,9 +4348,21 @@ public: { DXASSERT_NOMSG(ULE != nullptr); + const bool isQualified = ULE->getQualifier(); + + const bool isGlobalNamespace = + ULE->getQualifier() && + ULE->getQualifier()->getKind() == NestedNameSpecifier::Global; + + const bool isVkNamespace = + ULE->getQualifier() && + ULE->getQualifier()->getKind() == NestedNameSpecifier::Namespace && + ULE->getQualifier()->getAsNamespace()->getName() == "vk"; + // Intrinsics live in the global namespace, so references to their names // should be either unqualified or '::'-prefixed. - if (ULE->getQualifier() && ULE->getQualifier()->getKind() != NestedNameSpecifier::Global) { + // Exception: Vulkan-specific intrinsics live in the 'vk::' namespace. + if (isQualified && !isGlobalNamespace && !isVkNamespace) { return false; } @@ -4285,11 +4374,18 @@ public: } StringRef nameIdentifier = idInfo->getName(); + const HLSL_INTRINSIC *table = g_Intrinsics; + auto tableCount = _countof(g_Intrinsics); + if (isVkNamespace) { + table = g_VkIntrinsics; + tableCount = _countof(g_VkIntrinsics); + } IntrinsicDefIter cursor = FindIntrinsicByNameAndArgCount( - g_Intrinsics, _countof(g_Intrinsics), StringRef(), nameIdentifier, Args.size()); + table, tableCount, StringRef(), nameIdentifier, Args.size()); IntrinsicDefIter end = IntrinsicDefIter::CreateEnd( - g_Intrinsics, _countof(g_Intrinsics), IntrinsicTableDefIter::CreateEnd(m_intrinsicTables)); + table, tableCount, IntrinsicTableDefIter::CreateEnd(m_intrinsicTables)); + for (;cursor != end; ++cursor) { // If this is the intrinsic we're interested in, build up a representation @@ -4315,7 +4411,9 @@ public: if (insertedNewValue) { DXASSERT(tableName, "otherwise IDxcIntrinsicTable::GetTableName() failed"); - intrinsicFuncDecl = AddHLSLIntrinsicFunction(*m_context, m_hlslNSDecl, tableName, lowering, pIntrinsic, &functionArgTypes); + intrinsicFuncDecl = AddHLSLIntrinsicFunction( + *m_context, isVkNamespace ? m_vkNSDecl : m_hlslNSDecl, tableName, + lowering, pIntrinsic, &functionArgTypes); insertResult.first->setFunctionDecl(intrinsicFuncDecl); } else diff --git a/tools/clang/lib/Sema/SemaOverload.cpp b/tools/clang/lib/Sema/SemaOverload.cpp index e8ef7fedd..cb2b58dca 100644 --- a/tools/clang/lib/Sema/SemaOverload.cpp +++ b/tools/clang/lib/Sema/SemaOverload.cpp @@ -10914,7 +10914,18 @@ bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn, #ifndef NDEBUG if (ULE->requiresADL()) { // To do ADL, we must have found an unqualified name. - assert(!ULE->getQualifier() && "qualified name with ADL"); + // HLSL Change Begins + // + // We do want to allow argument-dependent lookup for intrinsic + // function names inside the "vk" namespace (which are by definition + // qualified names). + bool isVkNamespace = + ULE->getQualifier() && + ULE->getQualifier()->getKind() == NestedNameSpecifier::Namespace && + ULE->getQualifier()->getAsNamespace()->getName() == "vk"; + + assert((!ULE->getQualifier() || isVkNamespace) && "non-vk qualified name with ADL"); + // HLSL Change Ends // We don't perform ADL for implicit declarations of builtins. // Verify that this was correctly set up. diff --git a/tools/clang/lib/Sema/VkConstantsTables.h b/tools/clang/lib/Sema/VkConstantsTables.h new file mode 100644 index 000000000..8a9cee598 --- /dev/null +++ b/tools/clang/lib/Sema/VkConstantsTables.h @@ -0,0 +1,29 @@ +//===--- VkConstantsTables.h --- Implict Vulkan Constants Tables ---C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains information about implictly-defined vulkan constants. +// These constants will be added to the AST under the "vk" namespace. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_SEMA_VKCONSTANTSTABLES_H +#define LLVM_CLANG_LIB_SEMA_VKCONSTANTSTABLES_H + +std::vector> GetVkIntegerConstants() { + return { + {"CrossDeviceScope", 0u}, + {"DeviceScope", 1u}, + {"WorkgroupScope", 2u}, + {"SubgroupScope", 3u}, + {"InvocationScope", 4u}, + {"QueueFamilyScope", 5u}, + }; +} + +#endif // LLVM_CLANG_LIB_SEMA_VKCONSTANTSTABLES_H diff --git a/tools/clang/lib/Sema/gen_intrin_main_tables_15.h b/tools/clang/lib/Sema/gen_intrin_main_tables_15.h index 6f8e8bcec..c1e88c731 100644 --- a/tools/clang/lib/Sema/gen_intrin_main_tables_15.h +++ b/tools/clang/lib/Sema/gen_intrin_main_tables_15.h @@ -1724,6 +1724,25 @@ static const HLSL_INTRINSIC g_Intrinsics[] = {(UINT)hlsl::IntrinsicOp::IOP_trunc, false, true, false, -1, 2, g_Intrinsics_Args221}, }; +// +// Start of VkIntrinsics +// + +#ifdef ENABLE_SPIRV_CODEGEN + +static const HLSL_INTRINSIC_ARGUMENT g_VkIntrinsics_Args0[] = +{ + {"ReadClock", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_UINT64, 1, 1}, + {"scope", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1}, +}; + +static const HLSL_INTRINSIC g_VkIntrinsics[] = +{ + {(UINT)hlsl::IntrinsicOp::IOP_VkReadClock, false, false, false, -1, 2, g_VkIntrinsics_Args0}, +}; + +#endif // ENABLE_SPIRV_CODEGEN + // // Start of StreamMethods // @@ -6315,6 +6334,7 @@ static const UINT g_uTexture2DMethodsCount = 77; static const UINT g_uTexture3DMethodsCount = 24; static const UINT g_uTextureCUBEArrayMethodsCount = 42; static const UINT g_uTextureCUBEMethodsCount = 42; +static const UINT g_uVkIntrinsicsCount = 1; static const UINT g_uVkSubpassInputMSMethodsCount = 1; static const UINT g_uVkSubpassInputMethodsCount = 1; diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkcrossdevicescope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkcrossdevicescope.hlsl new file mode 100644 index 000000000..a93cfde76 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkcrossdevicescope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_0 + uint32_t scope = vk::CrossDeviceScope; + + return I.Output; +} + diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkdevicescope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkdevicescope.hlsl new file mode 100644 index 000000000..24a203b75 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkdevicescope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_1 + uint32_t scope = vk::DeviceScope; + + return I.Output; +} + diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkinvocationscope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkinvocationscope.hlsl new file mode 100644 index 000000000..fc49d8035 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkinvocationscope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_4 + uint32_t scope = vk::InvocationScope; + + return I.Output; +} + diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkqueuefamilyscope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkqueuefamilyscope.hlsl new file mode 100644 index 000000000..450526821 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkqueuefamilyscope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_5 + uint32_t scope = vk::QueueFamilyScope; + + return I.Output; +} + diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkreadclock.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkreadclock.hlsl new file mode 100644 index 000000000..ad1833e0d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkreadclock.hlsl @@ -0,0 +1,24 @@ +// Run: %dxc -T vs_6_0 -E main + +// CHECK: OpCapability ShaderClockKHR +// CHECK: OpExtension "SPV_KHR_shader_clock" + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + uint64_t clock; +// CHECK: {{%\d+}} = OpReadClockKHR %ulong %uint_1 + clock = vk::ReadClock(vk::DeviceScope); +// CHECK: {{%\d+}} = OpReadClockKHR %ulong %uint_3 + clock = vk::ReadClock(vk::SubgroupScope); + return I.Output; +} diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vksubgroupscope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vksubgroupscope.hlsl new file mode 100644 index 000000000..0716987cc --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vksubgroupscope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_3 + uint32_t scope = vk::SubgroupScope; + + return I.Output; +} + diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.vkworkgroupscope.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.vkworkgroupscope.hlsl new file mode 100644 index 000000000..282dbbab0 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.vkworkgroupscope.hlsl @@ -0,0 +1,21 @@ +// Run: %dxc -T vs_6_0 -E main + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + +// CHECK: OpStore %scope %uint_2 + uint32_t scope = vk::WorkgroupScope; + + return I.Output; +} + diff --git a/tools/clang/test/HLSLFileCheck/hlsl/namespace/vk-namespace.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/namespace/vk-namespace.hlsl new file mode 100644 index 000000000..3a1fe4899 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/namespace/vk-namespace.hlsl @@ -0,0 +1,21 @@ +// RUN: %dxc -E main -T vs_6_0 %s | FileCheck %s + +struct SInstanceData { + float4x3 VisualToWorld; + float4 Output; +}; + +struct VS_INPUT { + float3 Position : POSITION; + SInstanceData InstanceData : TEXCOORD4; +}; + +float4 main(const VS_INPUT v) : SV_Position { + const SInstanceData I = v.InstanceData; + uint64_t clock; +// CHECK: error: use of undeclared identifier 'vk' + clock = vk::ReadClock(vk::DeviceScope); +// CHECK: error: use of undeclared identifier 'vk' + clock = vk::ReadClock(vk::SubgroupScope); + return I.Output; +} diff --git a/tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp b/tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp index 8012d00d8..dc5fb4636 100644 --- a/tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp +++ b/tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp @@ -1234,6 +1234,29 @@ TEST_F(FileTest, IntrinsicsMultiPrefix) { runFileTest("intrinsics.multiprefix.hlsl", Expect::Failure); } +// Vulkan-specific intrinsic functions +TEST_F(FileTest, IntrinsicsVkCrossDeviceScope) { + runFileTest("intrinsics.vkcrossdevicescope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkDeviceScope) { + runFileTest("intrinsics.vkdevicescope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkWorkgroupScope) { + runFileTest("intrinsics.vkworkgroupscope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkSubgroupScope) { + runFileTest("intrinsics.vksubgroupscope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkInvocationScope) { + runFileTest("intrinsics.vkinvocationscope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkQueueFamilyScope) { + runFileTest("intrinsics.vkqueuefamilyscope.hlsl"); +} +TEST_F(FileTest, IntrinsicsVkReadClock) { + runFileTest("intrinsics.vkreadclock.hlsl"); +} + // For attributes TEST_F(FileTest, AttributeEarlyDepthStencil) { runFileTest("attribute.earlydepthstencil.ps.hlsl"); diff --git a/utils/hct/gen_intrin_main.txt b/utils/hct/gen_intrin_main.txt index 3e9a44efb..c13b7da0c 100644 --- a/utils/hct/gen_intrin_main.txt +++ b/utils/hct/gen_intrin_main.txt @@ -330,6 +330,15 @@ resource [[rn]] CreateResourceFromHeap(in uint index); } namespace + +// SPIRV Change Starts +namespace VkIntrinsics { + +u64 [[]] ReadClock(in uint scope); + +} namespace +// SPIRV Change Ends + namespace StreamMethods { void [[]] Append(in $match<-1, 1> void x) : stream_append; diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 0bf599c80..4f3f6bccf 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -2742,6 +2742,12 @@ class db_hlsl_intrinsic(object): self.ns_idx = ns_idx # Namespace index self.doc = doc # Documentation id_prefix = "IOP" if ns == "Intrinsics" else "MOP" + # SPIR-V Change Starts + if ns == "VkIntrinsics": + name = "Vk" + name + self.name = "Vk" + self.name + id_prefix = "IOP" + # SPIR-V Change Ends self.enum_name = "%s_%s" % (id_prefix, name) # enum name self.readonly = ro # Only read memory self.readnone = rn # Not read memory diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 80f46cde5..97916807e 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -676,7 +676,7 @@ def get_hlsl_intrinsics(): for i in sorted(db.intrinsics, key=lambda x: x.key): if last_ns != i.ns: last_ns = i.ns - id_prefix = "IOP" if last_ns == "Intrinsics" else "MOP" + id_prefix = "IOP" if last_ns == "Intrinsics" or last_ns == "VkIntrinsics" else "MOP" # SPIRV Change if (len(ns_table)): result += ns_table + "};\n" # SPIRV Change Starts