summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-string.h5
-rw-r--r--source/slang/core.meta.slang3
-rw-r--r--source/slang/hlsl.meta.slang1
-rw-r--r--source/slang/run-generators.vcxproj13
-rw-r--r--source/slang/run-generators.vcxproj.filters3
-rw-r--r--source/slang/slang-ast-modifier.h43
-rw-r--r--source/slang/slang-check-decl.cpp82
-rw-r--r--source/slang/slang-diagnostic-defs.h9
-rw-r--r--source/slang/slang-emit-c-like.cpp34
-rw-r--r--source/slang/slang-emit-c-like.h17
-rw-r--r--source/slang/slang-emit-cpp.cpp2
-rw-r--r--source/slang/slang-emit-cuda.cpp14
-rw-r--r--source/slang/slang-emit-cuda.h2
-rw-r--r--source/slang/slang-emit-glsl.cpp10
-rw-r--r--source/slang/slang-emit-glsl.h2
-rw-r--r--source/slang/slang-emit-hlsl.cpp93
-rw-r--r--source/slang/slang-emit-hlsl.h20
-rw-r--r--source/slang/slang-emit.cpp2
-rw-r--r--source/slang/slang-ir-inst-defs.h10
-rw-r--r--source/slang/slang-ir-insts.h31
-rw-r--r--source/slang/slang-ir-link.cpp39
-rw-r--r--source/slang/slang-lower-to-ir.cpp20
-rw-r--r--source/slang/slang-parameter-binding.cpp178
-rw-r--r--source/slang/slang-preprocessor.cpp139
-rw-r--r--source/slang/slang-preprocessor.h4
-rw-r--r--source/slang/slang.cpp5
-rw-r--r--source/slang/slang.vcxproj4
-rw-r--r--source/slang/slang.vcxproj.filters10
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 = &registerSemantic->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">