diff options
Diffstat (limited to 'source')
28 files changed, 720 insertions, 75 deletions
diff --git a/source/core/slang-string.h b/source/core/slang-string.h index 02a43a806..25bf99023 100644 --- a/source/core/slang-string.h +++ b/source/core/slang-string.h @@ -161,6 +161,11 @@ namespace Slang return !(*this == other); } + bool operator!=(char const* str) const + { + return (*this) != UnownedStringSlice(str, str + ::strlen(str)); + } + bool startsWith(UnownedStringSlice const& other) const; bool startsWith(char const* str) const; diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 8d0ce1d1d..f8b1bd1f9 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -2079,3 +2079,6 @@ attribute_syntax [anyValueSize(size:int)] : AnyValueSizeAttribute; __attributeTarget(DeclBase) attribute_syntax [builtin] : BuiltinAttribute; + +__attributeTarget(DeclBase) +attribute_syntax [__requiresNVAPI] : RequiresNVAPIAttribute; diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index aeadf8eba..7c5ca0027 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -285,6 +285,7 @@ ${{{{ __target_intrinsic(hlsl, "($3 = NvInterlockedAddFp32($0, $1, $2))") __cuda_sm_version(2.0) __target_intrinsic(cuda, "(*$3 = atomicAdd((float*)$0._getPtrAt($1), $2))") + [__requiresNVAPI] void InterlockedAddF32(uint byteAddress, float valueToAdd, out float originalValue); __specialized_for_target(glsl) diff --git a/source/slang/run-generators.vcxproj b/source/slang/run-generators.vcxproj index c03b67ce1..41f61f01d 100644 --- a/source/slang/run-generators.vcxproj +++ b/source/slang/run-generators.vcxproj @@ -209,6 +209,19 @@ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../bin/windows-x86/release/slang-embed.exe</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../bin/windows-x64/release/slang-embed.exe</AdditionalInputs> </CustomBuild> + <CustomBuild Include="..\..\prelude\slang-hlsl-prelude.h"> + <FileType>Document</FileType> + <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-embed" %(Identity)</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-embed" %(Identity)</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-embed" %(Identity)</Command> + <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-embed" %(Identity)</Command> + <Outputs>../../prelude/slang-hlsl-prelude.h.cpp</Outputs> + <Message>slang-embed %(Identity)</Message> + <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-embed.exe</AdditionalInputs> + <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-embed.exe</AdditionalInputs> + <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../bin/windows-x86/release/slang-embed.exe</AdditionalInputs> + <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../bin/windows-x64/release/slang-embed.exe</AdditionalInputs> + </CustomBuild> <CustomBuild Include="core.meta.slang"> <FileType>Document</FileType> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-generate" %(Identity)</Command> diff --git a/source/slang/run-generators.vcxproj.filters b/source/slang/run-generators.vcxproj.filters index 2f0200bd1..6a51d5ba7 100644 --- a/source/slang/run-generators.vcxproj.filters +++ b/source/slang/run-generators.vcxproj.filters @@ -26,6 +26,9 @@ <CustomBuild Include="..\..\prelude\slang-cuda-prelude.h"> <Filter>Header Files</Filter> </CustomBuild> + <CustomBuild Include="..\..\prelude\slang-hlsl-prelude.h"> + <Filter>Header Files</Filter> + </CustomBuild> <CustomBuild Include="core.meta.slang"> <Filter>Source Files</Filter> </CustomBuild> diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index 6a80d8722..cbdca6ab7 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -893,4 +893,47 @@ class AnyValueSizeAttribute : public Attribute int32_t size; }; + /// A `[__requiresNVAPI]` attribute indicates that the declaration being modifed + /// requires NVAPI operations for its implementation on D3D. +class RequiresNVAPIAttribute : public Attribute +{ + SLANG_CLASS(RequiresNVAPIAttribute) +}; + + /// Indicates that the modified declaration is one of the "magic" declarations + /// that NVAPI uses to communicate extended operations. When NVAPI is being included + /// via the prelude for downstream compilation, declarations with this modifier + /// will not be emitted, instead allowing the versions from the prelude to be used. +class NVAPIMagicModifier : public Modifier +{ + SLANG_CLASS(NVAPIMagicModifier) +}; + + /// A modifier that attaches to a `ModuleDecl` to indicate the register/space binding + /// that NVAPI wants to use, as indicated by, e.g., the `NV_SHADER_EXTN_SLOT` and + /// `NV_SHADER_EXTN_REGISTER_SPACE` preprocessor definitions. +class NVAPISlotModifier : public Modifier +{ + SLANG_CLASS(NVAPISlotModifier) + + /// The name of the register that is to be used (e.g., `"u3"`) + /// + /// This value will come from the `NV_SHADER_EXTN_SLOT` macro, if set. + /// + /// The `registerName` field must always be filled in when adding + /// an `NVAPISlotModifier` to a module; if no register name is defined, + /// then the modifier should not be added. + /// + String registerName; + + /// The name of the register space to be used (e.g., `space1`) + /// + /// This value will come from the `NV_SHADER_EXTN_REGISTER_SPACE` macro, + /// if set. + /// + /// It is valid for a user to specify a register name but not a space name, + /// and in that case `spaceName` will be set to `"space0"`. + String spaceName; +}; + } // namespace Slang diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 3ccf6fe06..a51dc0313 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -89,6 +89,7 @@ namespace Slang void visitPropertyDecl(PropertyDecl* decl); + void visitStructDecl(StructDecl* decl); /// Get the type of the storage accessed by an accessor. /// @@ -915,6 +916,87 @@ namespace Slang // validateArraySizeForVariable(varDecl); } + + // The NVAPI library allows user code to express extended operations + // (not supported natively by D3D HLSL) by communicating with + // a specially identified shader parameter called `g_NvidiaExt`. + // + // By default, that shader parameter would look like an ordinary + // global shader parameter to Slang, but we want to be able to + // associate special behavior with it to make downstream compilation + // work nicely (especially in the case where certain cross-platform + // operations in the Slang standard library need to use NVAPI). + // + // We will detect a global variable declaration that appears to + // be declaring `g_NvidiaExt` from NVAPI, and mark it with a special + // modifier to allow downstream steps to detect it whether or + // not it has an associated name. + // + if( as<ModuleDecl>(varDecl->parentDecl) + && varDecl->getName() + && varDecl->getName()->text == "g_NvidiaExt" ) + { + addModifier(varDecl, m_astBuilder->create<NVAPIMagicModifier>()); + } + // + // One thing that the `NVAPIMagicModifier` is going to do is ensure + // that `g_NvidiaExt` always gets emitted with *exactly* that name, + // whether or not obfuscation or other steps are enabled. + // + // The `g_NvidiaExt` variable is declared as a: + // + // RWStructuredBuffer<NvShaderExtnStruct> + // + // and we also want to make sure that the fields of that struct + // retain their original names in output code. We will detect + // variable declarations that represent fields of that struct + // and flag them as "magic" as well. + // + // Note: The goal here is to make it so that generated HLSL output + // can either use these declarations as they have been preocessed + // by the Slang front-end *or* they can use declarations directly + // from the NVAPI header during downstream compilation. + // + // TODO: It would be nice if we had a way to identify *all* of the + // declarations that come from the NVAPI header and mark them, so + // that the Slang front-end doesn't have to take responsibility + // for generating code from them (and can instead rely on the downstream + // compiler alone). + // + // The NVAPI header doesn't put any kind of macro-defined modifier + // (defaulting to an empty macro) in front of its declarations, + // so the most plausible way to add a modifier to all the declarations + // would be to tag the `nvHLSLExtns.h` header in a list of "magic" + // headers which should get all their declarations flagged during + // front-end processing, and then use the same header again during + // downstream compilation. + // + // For now, the current hackery seems a bit less complicated. + // + if( auto structDecl = as<StructDecl>(varDecl->parentDecl)) + { + if( structDecl->getName() + && structDecl->getName()->text == "NvShaderExtnStruct" ) + { + addModifier(varDecl, m_astBuilder->create<NVAPIMagicModifier>()); + } + } + } + + void SemanticsDeclHeaderVisitor::visitStructDecl(StructDecl* structDecl) + { + // As described above in `SemanticsDeclHeaderVisitor::checkVarDeclCommon`, + // we want to identify and tag the "magic" declarations that make NVAPI + // work, so that downstream passes can identify them and act accordingly. + // + // In this case, we are looking for the `NvShaderExtnStruct` type, which + // is used by `g_NvidiaExt`. + // + if( structDecl->getName() + && structDecl->getName()->text == "NvShaderExtnStruct" ) + { + addModifier(structDecl, m_astBuilder->create<NVAPIMagicModifier>()); + } } void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl) diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 044ce0830..d7f561537 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -555,6 +555,15 @@ DIAGNOSTIC(52003, Error, cppCompilerNotFound, "Could not find a suitable C/C++ c DIAGNOSTIC(52004, Error, unableToWriteFile, "Unable to write file '$0'") DIAGNOSTIC(52005, Error, unableToReadFile, "Unable to read file '$0'") +// +// 8xxxx - Issues specific to a particular library/technology/platform/etc. +// + +// 811xx - NVAPI + +DIAGNOSTIC(81110, Error, nvapiMacroMismatch, "conflicting definitions for NVAPI macro '$0': '$1' and '$2'") + + // 99999 - Internal compiler errors, and not-yet-classified diagnostics. DIAGNOSTIC(99999, Internal, unimplemented, "unimplemented feature in Slang compiler: $0") diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 9322c30bd..e0499319f 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -674,6 +674,18 @@ String CLikeSourceEmitter::generateName(IRInst* inst) return String(intrinsicDecoration->getDefinition()); } + // If the instruction reprsents one of the "magic" declarations + // that makes the NVAPI library work, then we want to make sure + // it uses the original name it was declared with, so that our + // generated code will work correctly with either a Slang-compiled + // or directly `#include`d version of those declarations during + // downstream compilation. + // + if(auto nvapiDecor = inst->findDecoration<IRNVAPIMagicDecoration>()) + { + return String(nvapiDecor->getName()); + } + auto entryPointDecor = inst->findDecoration<IREntryPointDecoration>(); if (entryPointDecor) { @@ -1997,12 +2009,23 @@ void CLikeSourceEmitter::_emitCallArgList(IRCall* inst) m_writer->emit(")"); } +void CLikeSourceEmitter::handleRequiredCapabilities(IRInst* inst) +{ + auto decoratedValue = inst; + while (auto specInst = as<IRSpecialize>(decoratedValue)) + { + decoratedValue = getSpecializedValue(specInst); + } + + handleRequiredCapabilitiesImpl(decoratedValue); +} + void CLikeSourceEmitter::emitCallExpr(IRCall* inst, EmitOpInfo outerPrec) { auto funcValue = inst->getOperand(0); // Does this function declare any requirements. - handleCallExprDecorationsImpl(funcValue); + handleRequiredCapabilities(funcValue); // We want to detect any call to an intrinsic operation, // that we can emit it directly without mangling, etc. @@ -3214,6 +3237,10 @@ void CLikeSourceEmitter::emitSimpleFuncImpl(IRFunc* func) emitEntryPointAttributes(func, entryPointDecor); } + // Deal with required features/capabilities of the function + // + handleRequiredCapabilitiesImpl(func); + emitFunctionPreambleImpl(func); auto name = getName(func); @@ -3735,6 +3762,11 @@ void CLikeSourceEmitter::emitGlobalParam(IRGlobalParam* varDecl) void CLikeSourceEmitter::emitGlobalInst(IRInst* inst) { + emitGlobalInstImpl(inst); +} + +void CLikeSourceEmitter::emitGlobalInstImpl(IRInst* inst) +{ m_writer->advanceToSourceLocation(inst->sourceLoc); switch(inst->op) diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 05cffd053..9c91078ee 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -270,6 +270,7 @@ public: void emitGlobalParam(IRGlobalParam* varDecl); void emitGlobalInst(IRInst* inst); + virtual void emitGlobalInstImpl(IRInst* inst); void ensureInstOperand(ComputeEmitActionsContext* ctx, IRInst* inst, EmitAction::Level requiredLevel = EmitAction::Level::Definition); @@ -282,6 +283,13 @@ public: void executeEmitActions(List<EmitAction> const& actions); void emitModule(IRModule* module) { m_irModule = module; emitModuleImpl(module); } + /// Emit any preprocessor directives that should come *before* the prelude code + /// + /// These are directives that are intended to customize some aspect(s) of the + /// prelude's behavior. + /// + void emitPreludeDirectives() { emitPreludeDirectivesImpl(); } + void emitPreprocessorDirectives() { emitPreprocessorDirectivesImpl(); } void emitSimpleType(IRType* type); @@ -308,6 +316,7 @@ public: virtual void emitImageFormatModifierImpl(IRInst* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); } virtual void emitLayoutQualifiersImpl(IRVarLayout* layout) { SLANG_UNUSED(layout); } + virtual void emitPreludeDirectivesImpl() {} virtual void emitPreprocessorDirectivesImpl() {} virtual void emitLayoutDirectivesImpl(TargetRequest* targetReq) { SLANG_UNUSED(targetReq); } virtual void emitRateQualifiersImpl(IRRate* rate) { SLANG_UNUSED(rate); } @@ -338,11 +347,15 @@ public: virtual void emitInterface(IRInterfaceType* interfaceType); virtual void emitRTTIObject(IRRTTIObject* rttiObject); - virtual void handleCallExprDecorationsImpl(IRInst* funcValue) { SLANG_UNUSED(funcValue); } - virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); return false; } virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) { SLANG_UNUSED(inst); SLANG_UNUSED(inOuterPrec); return false; } + /// Inspect the capabilities required by `inst` (according to its decorations), + /// and ensure that those capabilities have been detected and stored in the + /// target-specific extension tracker. + void handleRequiredCapabilities(IRInst* inst); + virtual void handleRequiredCapabilitiesImpl(IRInst* inst) { SLANG_UNUSED(inst); } + void _emitArrayType(IRArrayType* arrayType, EDeclarator* declarator); void _emitUnsizedArrayType(IRUnsizedArrayType* arrayType, EDeclarator* declarator); void _emitType(IRType* type, EDeclarator* declarator); diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 88afead1a..5359740b4 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -2184,7 +2184,7 @@ bool CPPSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOut auto funcValue = inst->getOperand(0); // Does this function declare any requirements. - handleCallExprDecorationsImpl(funcValue); + handleRequiredCapabilities(funcValue); // try doing automatically return _tryEmitInstExprAsIntrinsic(inst, inOuterPrec); diff --git a/source/slang/slang-emit-cuda.cpp b/source/slang/slang-emit-cuda.cpp index b029a8aa6..1e6df82ba 100644 --- a/source/slang/slang-emit-cuda.cpp +++ b/source/slang/slang-emit-cuda.cpp @@ -592,18 +592,12 @@ void CUDASourceEmitter::_requireCUDASMVersion(SemanticVersion const& version) } } -void CUDASourceEmitter::handleCallExprDecorationsImpl(IRInst* funcValue) +void CUDASourceEmitter::handleRequiredCapabilitiesImpl(IRInst* inst) { - // Does this function declare any requirements on GLSL version or - // extensions, which should affect our output? + // Does this function declare any requirements on CUDA capabilities + // that should affect output? - auto decoratedValue = funcValue; - while (auto specInst = as<IRSpecialize>(decoratedValue)) - { - decoratedValue = getSpecializedValue(specInst); - } - - for (auto decoration : decoratedValue->getDecorations()) + for (auto decoration : inst->getDecorations()) { if( auto smDecoration = as<IRRequireCUDASMVersionDecoration>(decoration)) { diff --git a/source/slang/slang-emit-cuda.h b/source/slang/slang-emit-cuda.h index 9378453ba..01fc3fb5b 100644 --- a/source/slang/slang-emit-cuda.h +++ b/source/slang/slang-emit-cuda.h @@ -64,7 +64,7 @@ protected: virtual void emitLoopControlDecorationImpl(IRLoopControlDecoration* decl) SLANG_OVERRIDE; - virtual void handleCallExprDecorationsImpl(IRInst* funcValue) SLANG_OVERRIDE; + virtual void handleRequiredCapabilitiesImpl(IRInst* inst) SLANG_OVERRIDE; virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index d09c778b9..cc2494455 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -1452,18 +1452,12 @@ bool GLSLSourceEmitter::tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOu return false; } -void GLSLSourceEmitter::handleCallExprDecorationsImpl(IRInst* funcValue) +void GLSLSourceEmitter::handleRequiredCapabilitiesImpl(IRInst* inst) { // Does this function declare any requirements on GLSL version or // extensions, which should affect our output? - auto decoratedValue = funcValue; - while (auto specInst = as<IRSpecialize>(decoratedValue)) - { - decoratedValue = getSpecializedValue(specInst); - } - - for (auto decoration : decoratedValue->getDecorations()) + for (auto decoration : inst->getDecorations()) { switch (decoration->op) { diff --git a/source/slang/slang-emit-glsl.h b/source/slang/slang-emit-glsl.h index 5c46d5471..56da1b064 100644 --- a/source/slang/slang-emit-glsl.h +++ b/source/slang/slang-emit-glsl.h @@ -44,7 +44,7 @@ protected: virtual void emitVarDecorationsImpl(IRInst* varDecl) SLANG_OVERRIDE; virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE; - virtual void handleCallExprDecorationsImpl(IRInst* funcValue) SLANG_OVERRIDE; + virtual void handleRequiredCapabilitiesImpl(IRInst* inst) SLANG_OVERRIDE; virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE; virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index 036c4701a..57d603940 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -1001,5 +1001,98 @@ void HLSLSourceEmitter::emitMatrixLayoutModifiersImpl(IRVarLayout* layout) } } +void HLSLSourceEmitter::handleRequiredCapabilitiesImpl(IRInst* inst) +{ + if(inst->findDecoration<IRRequiresNVAPIDecoration>()) + { + m_extensionTracker->m_requiresNVAPI = true; + } +} + +void HLSLSourceEmitter::emitPreludeDirectivesImpl() +{ + if( m_extensionTracker->m_requiresNVAPI ) + { + // If the generated code includes implicit NVAPI use, + // then we need to ensure that NVAPI support is included + // via the prelude. + // + m_writer->emit("#define SLANG_HLSL_ENABLE_NVAPI 1\n"); + + // In addition, if the user has informed the Slang compiler of + // the register/space that it wants to use for NVAPI, then we + // need to pass along that information to prelude in the + // generated code, so that it can be picked up by the NVAPI + // header at the point where it gets included. + // + // Note: If the user doesn't inform the Slang compiler where + // it wants the NVAPI parameter to be bound, then a downstream + // compiler error is going to occur. We could try to produce + // our own error message here, but our error is unlikely to + // be significantly better, and also it is *technically* + // possible for the user to use Slang to generate HLSL, + // and then go on to compile it manually via fxc/dxc, where + // they could pass in these `#define`s using command-line + // or API options. + // + if( auto decor = m_irModule->getModuleInst()->findDecoration<IRNVAPISlotDecoration>() ) + { + m_writer->emit("#define NV_SHADER_EXTN_SLOT "); + m_writer->emit(decor->getRegisterName()); + m_writer->emit("\n"); + + // Note: We only emit a preprocessor directive if the space + // is not `space0`, because we want to ensure that the output + // code can compile with fxc when possible (and fxc has no + // understanding of `space`s). + // + auto spaceName = decor->getSpaceName(); + if( spaceName != "space0" ) + { + m_writer->emit("#define NV_SHADER_EXTN_REGISTER_SPACE "); + m_writer->emit(spaceName); + m_writer->emit("\n"); + } + } + } +} + +void HLSLSourceEmitter::emitGlobalInstImpl(IRInst* inst) +{ + if( auto nvapiDecor = inst->findDecoration<IRNVAPIMagicDecoration>() ) + { + // When emitting one of the "magic" NVAPI declarations, + // we will wrap it in a preprocessor conditional that + // skips it if the NVAPI header is already being included + // via the prelude. In that case, the definitions from + // the prelude-included NVAPI will be used instead of + // those that were processed by the Slang front-end. + // + // TODO: In theory we could drop the downstream preprocessor + // conditional here, and either emit or not emit the + // instruction based on whether the code needs NVAPI (which + // is when `SLANG_HLSL_ENABLE_NVAPI` would be set). + // Such a change would require that we replace the current + // approach of tracking extension use during emit with an + // approach that detects requirements as a pure pre-pass. + // + // Note: We skip `IRStructKey` instructions here because + // the fields of the `NvShaderExtnStruct` are also decorated, + // but field keys don't produce anything in the output, so + // we'd have conditionals that are wrapping empty lines. + // + if( !as<IRStructKey>(inst) ) + { + m_writer->emit("#ifndef SLANG_HLSL_ENABLE_NVAPI\n"); + Super::emitGlobalInstImpl(inst); + m_writer->emit("#endif\n"); + return; + } + } + + Super::emitGlobalInstImpl(inst); +} + + } // namespace Slang diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h index f9b0ad0fb..b93cc694b 100644 --- a/source/slang/slang-emit-hlsl.h +++ b/source/slang/slang-emit-hlsl.h @@ -7,16 +7,27 @@ namespace Slang { +class HLSLExtensionTracker : public RefObject +{ +public: + /// Has any operation been used that requires NVAPI to be included via prelude? + bool m_requiresNVAPI = false; +}; + class HLSLSourceEmitter : public CLikeSourceEmitter { public: typedef CLikeSourceEmitter Super; - HLSLSourceEmitter(const Desc& desc) : - Super(desc) + HLSLSourceEmitter(const Desc& desc) + : Super(desc) + , m_extensionTracker(new HLSLExtensionTracker) {} + virtual RefObject* getExtensionTracker() SLANG_OVERRIDE { return m_extensionTracker; } + protected: + RefPtr<HLSLExtensionTracker> m_extensionTracker; virtual void emitLayoutSemanticsImpl(IRInst* inst, char const* uniformSemanticSpelling) SLANG_OVERRIDE; virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) SLANG_OVERRIDE; @@ -35,6 +46,11 @@ protected: virtual void emitSimpleValueImpl(IRInst* inst) SLANG_OVERRIDE; virtual void emitLoopControlDecorationImpl(IRLoopControlDecoration* decl) SLANG_OVERRIDE; + virtual void handleRequiredCapabilitiesImpl(IRInst* inst) SLANG_OVERRIDE; + virtual void emitPreludeDirectivesImpl() SLANG_OVERRIDE; + + virtual void emitGlobalInstImpl(IRInst* inst) SLANG_OVERRIDE; + // Emit a single `register` semantic, as appropriate for a given resource-type-specific layout info // Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`) void _emitHLSLRegisterSemantic(LayoutResourceKind kind, EmitVarChain* chain, char const* uniformSemanticSpelling = "register"); diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 4b7b13e4f..fbddd2910 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -825,6 +825,8 @@ SlangResult emitEntryPointsSourceFromIR( // Now that we've emitted the code for all the declarations in the file, // it is time to stitch together the final output. + sourceEmitter->emitPreludeDirectives(); + { // If there is a prelude emit it const auto& prelude = compileRequest->getSession()->getPreludeForLanguage(sourceLanguage); diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 7863b9a9c..c7d223ab6 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -565,6 +565,16 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(BuiltinDecoration, BuiltinDecoration, 0, 0) + /// The decorated instruction requires NVAPI to be included via prelude when compiling for D3D. + INST(RequiresNVAPIDecoration, requiresNVAPI, 0, 0) + + /// The decorated instruction is part of the NVAPI "magic" and should always use its original name + INST(NVAPIMagicDecoration, nvapiMagic, 1, 0) + + /// A decoration that applies to an entire IR module, and indicates the register/space binding + /// that the NVAPI shader parameter intends to use. + INST(NVAPISlotDecoration, nvapiSlot, 2, 0) + INST(SemanticDecoration, semantic, 2, 0) INST_RANGE(Decoration, HighLevelDeclDecoration, SemanticDecoration) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 3b390015b..ef382373f 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -260,7 +260,28 @@ IR_SIMPLE_DECORATION(GloballyCoherentDecoration) IR_SIMPLE_DECORATION(PreciseDecoration) IR_SIMPLE_DECORATION(PublicDecoration) IR_SIMPLE_DECORATION(KeepAliveDecoration) +IR_SIMPLE_DECORATION(RequiresNVAPIDecoration) +struct IRNVAPIMagicDecoration : IRDecoration +{ + enum { kOp = kIROp_NVAPIMagicDecoration }; + IR_LEAF_ISA(NVAPIMagicDecoration) + + IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); } + UnownedStringSlice getName() { return getNameOperand()->getStringSlice(); } +}; + +struct IRNVAPISlotDecoration : IRDecoration +{ + enum { kOp = kIROp_NVAPISlotDecoration }; + IR_LEAF_ISA(NVAPISlotDecoration) + + IRStringLit* getRegisterNameOperand() { return cast<IRStringLit>(getOperand(0)); } + UnownedStringSlice getRegisterName() { return getRegisterNameOperand()->getStringSlice(); } + + IRStringLit* getSpaceNameOperand() { return cast<IRStringLit>(getOperand(1)); } + UnownedStringSlice getSpaceName() { return getSpaceNameOperand()->getStringSlice(); } +}; struct IROutputControlPointsDecoration : IRDecoration { @@ -2457,6 +2478,16 @@ struct IRBuilder addDecoration(value, kIROp_PublicDecoration); } + void addNVAPIMagicDecoration(IRInst* value, UnownedStringSlice const& name) + { + addDecoration(value, kIROp_NVAPIMagicDecoration, getStringValue(name)); + } + + void addNVAPISlotDecoration(IRInst* value, UnownedStringSlice const& registerName, UnownedStringSlice const& spaceName) + { + addDecoration(value, kIROp_NVAPISlotDecoration, getStringValue(registerName), getStringValue(spaceName)); + } + /// Add a decoration that indicates that the given `inst` depends on the given `dependency`. /// /// This decoration can be used to ensure that a value that an instruction diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index f40f83fb8..802288c0b 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -1499,6 +1499,45 @@ LinkedIR linkIR( } } + // It is possible that metadata has been attached to the input modules + // themselves, which should be copied over to the output module. + // + // In cases where multiple input modules specify the same metadata + // decoration, we will need rules to merge such decorations according + // to opcode-specific policy (e.g., a `[maxStackSizeRequired(...)]` + // decoration might use a maximum over all specified values, while a + // `[assumedWaveSize(...)]` decoration might require that all specified + // values match exactly). + // + for (IRModule* irModule : irModules) + { + for( auto decoration : irModule->getModuleInst()->getDecorations() ) + { + switch( decoration->op ) + { + case kIROp_NVAPISlotDecoration: + { + // For now we just clone every decoration we see, + // which means that an arbitrary one will end up + // "winning" and being the one found by searches + // in later code. + // + // TODO: need validation to check if decorations are + // consistent with one another, in the case where + // multiple input modules have matching decorations. + // + auto cloned = cloneInst(context, context->builder, decoration); + cloned->insertAtStart(state->irModule->getModuleInst()); + } + break; + + default: + break; + } + } + } + + // TODO: *technically* we should consider the case where // we have global variables with initializers, since // these should get run whether or not the entry point diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 424358d3c..e9381268b 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5480,6 +5480,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> builder->addHighLevelDeclDecoration(irParam, decl); } + addTargetIntrinsicDecorations(irParam, decl); + // A global variable's SSA value is a *pointer* to // the underlying storage. setGlobalValue(context, decl, paramVal); @@ -6533,6 +6535,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> builder->addTargetIntrinsicDecoration(irInst, targetName, definition.getUnownedSlice()); } + + if(auto nvapiMod = decl->findModifier<NVAPIMagicModifier>()) + { + builder->addNVAPIMagicDecoration(irInst, decl->getName()->text.getUnownedSlice()); + } } /// Is `decl` a member function (or effectively a member function) when considered as a stdlib declaration? @@ -7000,6 +7007,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> getBuilder()->addRequireCUDASMVersionDecoration(irFunc, versionMod->version); } + if(decl->findModifier<RequiresNVAPIAttribute>()) + { + getBuilder()->addSimpleDecoration<IRRequiresNVAPIDecoration>(irFunc); + } + if (decl->findModifier<PublicModifier>()) { getBuilder()->addSimpleDecoration<IRPublicDecoration>(irFunc); } @@ -7662,6 +7674,14 @@ IRModule* generateIRForTranslationUnit( } } + if(auto nvapiSlotModifier = translationUnit->getModuleDecl()->findModifier<NVAPISlotModifier>()) + { + builder->addNVAPISlotDecoration( + module->getModuleInst(), + nvapiSlotModifier->registerName.getUnownedSlice(), + nvapiSlotModifier->spaceName.getUnownedSlice()); + } + #if 0 { DiagnosticSinkWriter writer(compileRequest->getSink()); diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 90b5d9824..73c94f722 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -377,6 +377,9 @@ struct SharedParameterBindingContext // The space to use for auto-generated bindings. UInt defaultSpace = 0; + // Any NVAPI slot binding information that has been generated + List<NVAPISlotModifier*> nvapiSlotModifiers; + TargetRequest* getTargetRequest() { return targetRequest; } DiagnosticSink* getSink() { return m_sink; } Linkage* getLinkage() { return targetRequest->getLinkage(); } @@ -483,16 +486,19 @@ LayoutResourceKind findRegisterClassFromName(UnownedStringSlice const& registerC return LayoutResourceKind::None; } -LayoutSemanticInfo ExtractLayoutSemanticInfo( - ParameterBindingContext* context, - HLSLLayoutSemantic* semantic) +LayoutSemanticInfo extractHLSLLayoutSemanticInfo( + UnownedStringSlice registerName, + SourceLoc registerLoc, + UnownedStringSlice spaceName, + SourceLoc spaceLoc, + DiagnosticSink* sink + ) { LayoutSemanticInfo info; info.space = 0; info.index = 0; info.kind = LayoutResourceKind::None; - UnownedStringSlice registerName = semantic->registerName.getContent(); if (registerName.getLength() == 0) return info; @@ -513,7 +519,7 @@ LayoutSemanticInfo ExtractLayoutSemanticInfo( LayoutResourceKind kind = findRegisterClassFromName(registerClassName); if(kind == LayoutResourceKind::None) { - getSink(context)->diagnose(semantic->registerName, Diagnostics::unknownRegisterClass, registerClassName); + sink->diagnose(registerLoc, Diagnostics::unknownRegisterClass, registerClassName); return info; } @@ -521,7 +527,7 @@ LayoutSemanticInfo ExtractLayoutSemanticInfo( // how it works for varying input/output semantics). if( registerIndexDigits.getLength() == 0 ) { - getSink(context)->diagnose(semantic->registerName, Diagnostics::expectedARegisterIndex, registerClassName); + sink->diagnose(registerLoc, Diagnostics::expectedARegisterIndex, registerClassName); } UInt index = 0; @@ -531,49 +537,67 @@ LayoutSemanticInfo ExtractLayoutSemanticInfo( index = index * 10 + (c - '0'); } - UInt space = 0; - if( auto registerSemantic = as<HLSLRegisterSemantic>(semantic) ) + if(spaceName.getLength() != 0) { - auto const& spaceName = registerSemantic->spaceName.getContent(); - if(spaceName.getLength() != 0) - { - UnownedStringSlice spaceSpelling; - UnownedStringSlice spaceDigits; - splitNameAndIndex(spaceName, spaceSpelling, spaceDigits); + UnownedStringSlice spaceSpelling; + UnownedStringSlice spaceDigits; + splitNameAndIndex(spaceName, spaceSpelling, spaceDigits); - if( kind == LayoutResourceKind::RegisterSpace ) - { - getSink(context)->diagnose(registerSemantic->spaceName, Diagnostics::unexpectedSpecifierAfterSpace, spaceName); - } - else if( spaceSpelling != UnownedTerminatedStringSlice("space") ) - { - getSink(context)->diagnose(registerSemantic->spaceName, Diagnostics::expectedSpace, spaceSpelling); - } - else if( spaceDigits.getLength() == 0 ) - { - getSink(context)->diagnose(registerSemantic->spaceName, Diagnostics::expectedSpaceIndex); - } - else + if( kind == LayoutResourceKind::RegisterSpace ) + { + sink->diagnose(spaceLoc, Diagnostics::unexpectedSpecifierAfterSpace, spaceName); + } + else if( spaceSpelling != UnownedTerminatedStringSlice("space") ) + { + sink->diagnose(spaceLoc, Diagnostics::expectedSpace, spaceSpelling); + } + else if( spaceDigits.getLength() == 0 ) + { + sink->diagnose(spaceLoc, Diagnostics::expectedSpaceIndex); + } + else + { + for(auto c : spaceDigits) { - for(auto c : spaceDigits) - { - SLANG_ASSERT(isDigit(c)); - space = space * 10 + (c - '0'); - } + SLANG_ASSERT(isDigit(c)); + space = space * 10 + (c - '0'); } } } + info.kind = kind; + info.index = (int) index; + info.space = space; + return info; +} + +LayoutSemanticInfo ExtractLayoutSemanticInfo( + ParameterBindingContext* context, + HLSLLayoutSemantic* semantic) +{ + Token const& registerToken = semantic->registerName; + + Token defaultSpaceToken; + Token const* spaceToken = &defaultSpaceToken; + if( auto registerSemantic = as<HLSLRegisterSemantic>(semantic) ) + { + spaceToken = ®isterSemantic->spaceName; + } + + LayoutSemanticInfo info = extractHLSLLayoutSemanticInfo( + registerToken.getContent(), + registerToken.loc, + spaceToken->getContent(), + spaceToken->loc, + getSink(context)); + // TODO: handle component mask part of things... if( semantic->componentMask.hasContent()) { getSink(context)->diagnose(semantic->componentMask, Diagnostics::componentMaskNotSupported); } - info.kind = kind; - info.index = (int) index; - info.space = space; return info; } @@ -2886,6 +2910,14 @@ struct CollectParametersVisitor : ComponentTypeVisitor collectGlobalScopeParameter(m_context, shaderParamInfo, SubstitutionSet()); } + + if( auto moduleDecl = module->getModuleDecl() ) + { + if( auto nvapiSlotModifier = moduleDecl->findModifier<NVAPISlotModifier>() ) + { + m_context->shared->nvapiSlotModifiers.add(nvapiSlotModifier); + } + } } }; @@ -3361,6 +3393,82 @@ RefPtr<ProgramLayout> generateParameterBindings( generateParameterBindings(&context, parameter); } + // It is possible that code has specified an explicit location + // for the UAV used to communicate with NVAPI, but the code + // is not actually including/using the NVAPI header. We still + // need to ensure that user-defined shader parameters do not + // conflict with the location that will be used by NVAPI. + // + if( isD3DTarget(targetReq) ) + { + // Information about the NVAPI parameter was recorded + // on the AST `ModuleDecl`s during earlier stages of + // compilation, and there might be multiple modules + // as input to layout and back-end compilation. + // + // We do not take responsibility for diagnostic conflicts + // at this point, and simply process all of the modifiers + // we see. + // + for( auto nvapiSlotModifier : sharedContext.nvapiSlotModifiers ) + { + // For a given modifier, we start by parsing the semantic + // strings that were specified, just as we would if they + // had been specified via a `register(...)` semantic. + // + auto info = extractHLSLLayoutSemanticInfo( + nvapiSlotModifier->registerName.getUnownedSlice(), + nvapiSlotModifier->loc, + nvapiSlotModifier->spaceName.getUnownedSlice(), + nvapiSlotModifier->loc, + sink); + auto kind = info.kind; + if (kind == LayoutResourceKind::None) + continue; + + // The NVAPI parameter always uses a single register. + // + LayoutSize count = 1; + + // We are going to mark the register range declared for the + // NVAPI parameter as used. + // + // Note: It is possible that the range has *already* been + // marked as used, because the `g_NvidiaExt` parameter has + // already been processed by the `generateParameterBindings` + // logic above. We don't worry about that case and do not + // diagnose an error. + // + // Note: It is *also* possible that some non-NVAPI parameter + // with an explicit binding will collide with the NVAPI + // parameter, and we also do not diagnose a problem in + // that case. + // + // TODO: We could probably make the user experience nicer + // here in the case of conflicts, but we are already deep + // into a do-what-I-mean edge case. + // + // Note: In the case where the user sets up the NVAPI + // register/space via the front-end (e.g., by setting a + // `NV_SHADER_EXTN_SLOT` macro), but doesn't actually + // `#include` the NVAPI header, we will *still* reserve + // the appropriate register so that it won't be used + // by user paraemter. + // + // That policy means that simply defining a particular + // macro can alter layout behavior, which is conceptually + // kind of a mess, but also seems to be the best possible + // answer given the constraints. + // + auto usedRangeSet = findUsedRangeSetForSpace(&context, info.space); + markSpaceUsed(&context, nullptr, info.space); + usedRangeSet->usedResourceRanges[(int)kind].Add( + nullptr, + info.index, + info.index + count); + } + } + // Once we have a canonical list of all the parameters, we can // detect if there are any global-scope parameters that make use // of `LayoutResourceKind::Uniform`, since such parameters would diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp index fceee27f9..5a54bfcb0 100644 --- a/source/slang/slang-preprocessor.cpp +++ b/source/slang/slang-preprocessor.cpp @@ -222,6 +222,9 @@ struct Preprocessor /// The module, if any, that the preprocessed result will belong to Module* parentModule = nullptr; + /// The AST builder that should be used when creating AST nodes for `parentModule` + ASTBuilder* astBuilder = nullptr; + // The unique identities of any paths that have issued `#pragma once` directives to // stop them from being included again. HashSet<String> pragmaOnceUniqueIdentities; @@ -2447,18 +2450,141 @@ static TokenList ReadAllTokens( return tokens; } + /// Try to look up a macro with the given `macroName` and produce its value as a string +static bool _findMacroValue( + Preprocessor* preprocessor, + char const* macroName, + String& outValue, + SourceLoc& outLoc) +{ + auto namePool = preprocessor->linkage->getNamePool(); + auto macro = LookupMacro(preprocessor, namePool->getName(macroName)); + if(!macro) + return false; + if(macro->flavor != PreprocessorMacroFlavor::ObjectLike) + return false; + + MacroExpansion* expansion = new MacroExpansion(); + initializeMacroExpansion(preprocessor, expansion, macro); + pushMacroExpansion(preprocessor, expansion); + + String value; + for(bool first = true;;first = false) + { + Token token = ReadToken(preprocessor); + if(token.type == TokenType::EndOfFile) + break; + + if(!first && (token.flags & TokenFlag::AfterWhitespace)) + value.append(" "); + value.append(token.getContent()); + } + + outValue = value; + outLoc = macro->getLoc(); + return true; +} + + /// Validate that a re-defintion of an NVAPI-related macro matches any previous definition +static void _validateNVAPIMacroMatch( + Preprocessor* preprocessor, + char const* macroName, + String const& existingValue, + String const& newValue, + SourceLoc loc) +{ + if( existingValue != newValue ) + { + preprocessor->sink->diagnose(loc, Diagnostics::nvapiMacroMismatch, macroName, existingValue, newValue); + } +} + + /// Collect macro definitions that are relevant to subsequent compilation steps, and store them +static void _collectDownstreamRelevantMacros( + Preprocessor* preprocessor) +{ + // For now, the only case of semantically-relevant macros we need to worrry + // about are the NVAPI macros used to establish the register/space to use. + // + static const char* kNVAPIRegisterMacroName = "NV_SHADER_EXTN_SLOT"; + static const char* kNVAPISpaceMacroName = "NV_SHADER_EXTN_REGISTER_SPACE"; + + // For NVAPI use, the `NV_SHADER_EXTN_SLOT` macro is required to be defined. + // + String nvapiRegister; + SourceLoc nvapiRegisterLoc; + if(_findMacroValue(preprocessor, kNVAPIRegisterMacroName, nvapiRegister, nvapiRegisterLoc)) + { + // In contrast, NVAPI can be used without defining `NV_SHADER_EXTN_REGISTER_SPACE`, + // which effectively defaults to `space0`. + // + String nvapiSpace = "space0"; + SourceLoc nvapiSpaceLoc; + _findMacroValue(preprocessor, kNVAPISpaceMacroName, nvapiSpace, nvapiSpaceLoc); + + // We are going to store the values of these macros on the AST-level `ModuleDecl` + // so that they will be available to later processing stages. + // + // Note: An alternative design here would be to either put the data directly + // on the `Module`, or to define some kind of side-channel output that the + // preprocessor can use to communicate these macro values back (and then + // allow another system to create the AST nodes). In practice, all of these + // alternatives would actually increase the amount of code/complexity, + // so we stick with the simple-but-hacky option of having the + // preprocessor create AST nodes directly. + // + auto module = preprocessor->parentModule; + if(!module) return; + auto moduleDecl = module->getModuleDecl(); + + // We need to make sure that the AST nodes we create will have the right + // lifetime (it should match the module we are adding to). + // + auto astBuilder = preprocessor->astBuilder; + if(!astBuilder) return; + + if(auto existingModifier = moduleDecl->findModifier<NVAPISlotModifier>()) + { + // If there is already a modifier attached to the module (perhaps + // because of preprocessing a different source file, or because + // of settings established via command-line options), then we + // need to validate that the values being set in this file + // match those already set (or else there is likely to be + // some kind of error in the user's code). + // + _validateNVAPIMacroMatch(preprocessor, kNVAPIRegisterMacroName, existingModifier->registerName, nvapiRegister, nvapiRegisterLoc); + _validateNVAPIMacroMatch(preprocessor, kNVAPISpaceMacroName, existingModifier->spaceName, nvapiSpace, nvapiSpaceLoc); + } + else + { + // If there is no existing modifier on the module, then we + // take responsibility for adding one, based on the macro + // values we saw. + // + auto modifier = astBuilder->create<NVAPISlotModifier>(); + modifier->loc = nvapiRegisterLoc; + modifier->registerName = nvapiRegister; + modifier->spaceName = nvapiSpace; + + addModifier(moduleDecl, modifier); + } + } +} + TokenList preprocessSource( SourceFile* file, DiagnosticSink* sink, IncludeSystem* includeSystem, Dictionary<String, String> defines, Linkage* linkage, - Module* parentModule) + Module* parentModule, + ASTBuilder* astBuilder) { Preprocessor preprocessor; InitializePreprocessor(&preprocessor, sink); preprocessor.linkage = linkage; preprocessor.parentModule = parentModule; + preprocessor.astBuilder = astBuilder; preprocessor.includeSystem = includeSystem; for (auto p : defines) @@ -2475,6 +2601,17 @@ TokenList preprocessSource( TokenList tokens = ReadAllTokens(&preprocessor); + // We look at the preprocessor state after reading the entire + // source file/string, in order to see if any macros have been + // set that should be considered semantically relevant for + // later stages of compilation. + // + // Note: Checking the macro environment *after* preprocessing is complete + // means that we can treat macros introduced via `-D` options or the API + // equivalently to macros introduced via `#define`s in user code. + // + _collectDownstreamRelevantMacros(&preprocessor); + FinalizePreprocessor(&preprocessor); // debugging: build the pre-processed source back together diff --git a/source/slang/slang-preprocessor.h b/source/slang/slang-preprocessor.h index 472a8675d..7c72a859c 100644 --- a/source/slang/slang-preprocessor.h +++ b/source/slang/slang-preprocessor.h @@ -10,6 +10,7 @@ namespace Slang { +class ASTBuilder; class DiagnosticSink; class Linkage; class Module; @@ -22,7 +23,8 @@ TokenList preprocessSource( IncludeSystem* includeSystem, Dictionary<String, String> defines, Linkage* linkage, - Module* parentModule); + Module* parentModule, + ASTBuilder* astBuilder = nullptr); } // namespace Slang diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index fbdbe14b6..0add5acea 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -42,6 +42,7 @@ #endif extern char const* slang_cuda_prelude; +extern char const* slang_hlsl_prelude; namespace Slang { @@ -189,6 +190,7 @@ void Session::init() // Set up default prelude code for target languages that need a prelude m_languagePreludes[Index(SourceLanguage::CUDA)] = slang_cuda_prelude; + m_languagePreludes[Index(SourceLanguage::HLSL)] = slang_hlsl_prelude; } ISlangUnknown* Session::getInterface(const Guid& guid) @@ -989,7 +991,8 @@ void FrontEndCompileRequest::parseTranslationUnit( &includeSystem, combinedPreprocessorDefinitions, getLinkage(), - module); + module, + astBuilder); parseSourceFile( astBuilder, diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index a0865d16c..a5d51e36b 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -298,10 +298,8 @@ <ClInclude Include="slang-visitor.h" /> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\prelude\slang-cpp-prelude.h.cpp" /> - <ClCompile Include="..\..\prelude\slang-cpp-scalar-intrinsics.h.cpp" /> - <ClCompile Include="..\..\prelude\slang-cpp-types.h.cpp" /> <ClCompile Include="..\..\prelude\slang-cuda-prelude.h.cpp" /> + <ClCompile Include="..\..\prelude\slang-hlsl-prelude.h.cpp" /> <ClCompile Include="slang-ast-builder.cpp" /> <ClCompile Include="slang-ast-decl.cpp" /> <ClCompile Include="slang-ast-dump.cpp" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 5edd4d31e..fec82a8f5 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -341,16 +341,10 @@ </ClInclude> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\prelude\slang-cpp-prelude.h.cpp"> - <Filter>Header Files</Filter> - </ClCompile> - <ClCompile Include="..\..\prelude\slang-cpp-scalar-intrinsics.h.cpp"> - <Filter>Header Files</Filter> - </ClCompile> - <ClCompile Include="..\..\prelude\slang-cpp-types.h.cpp"> + <ClCompile Include="..\..\prelude\slang-cuda-prelude.h.cpp"> <Filter>Header Files</Filter> </ClCompile> - <ClCompile Include="..\..\prelude\slang-cuda-prelude.h.cpp"> + <ClCompile Include="..\..\prelude\slang-hlsl-prelude.h.cpp"> <Filter>Header Files</Filter> </ClCompile> <ClCompile Include="slang-ast-builder.cpp"> |
