summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-05-03 20:16:58 -0400
committerGitHub <noreply@github.com>2023-05-03 20:16:58 -0400
commitc9ef8d58a135061262fd321a82061d27dc733cf5 (patch)
tree888bee92d8519fe80ec9d5dbb95c2dc8da985865
parentbf2b05528d6edbbf1beb87552f447d29f92eee47 (diff)
HLSL->Vulkan binding support (#2865)
* WIP around VK shift binding. * Refactor around options parsing. * Remove needless passing around of sink. * Some more tidying around OptionsParser. * Handle vulkan shift parsing. * Fix small issue around vk binding and "all". * Fixing some small issues. Missing break. * Split out VulkanLayoutOptions * WIP binding taking into account HLSL->Vulkan options. * First attempt at making binding work with HLSLVulkanOptions. * VulkanLayoutOptions -> HLSLToVulkanLayoutOptions * WIP with HLSL-Vulkan binding. * Some more testing around vk-shift. * Improvements around global binding. More tests. * Improve test coverage. Improve checking for requirements around default space. * Update command line options. * Small fixes. * Small fix in options reporting. * Fix warning issue. * Some fixes for isDefault for HLSLToVulkanLayoutOptions. * Update hlsl-to-vulkan-shift output. The difference was due to default handling if shift isn't specified, and not being specified was not correctly tracked.
-rw-r--r--build/visual-studio/slang/slang.vcxproj2
-rw-r--r--build/visual-studio/slang/slang.vcxproj.filters6
-rw-r--r--docs/command-line-slangc-reference.md37
-rw-r--r--source/compiler-core/slang-command-line-args.h16
-rw-r--r--source/compiler-core/slang-diagnostic-sink.h2
-rwxr-xr-xsource/slang/slang-compiler.h13
-rw-r--r--source/slang/slang-diagnostic-defs.h1
-rw-r--r--source/slang/slang-hlsl-to-vulkan-layout-options.cpp132
-rw-r--r--source/slang/slang-hlsl-to-vulkan-layout-options.h85
-rw-r--r--source/slang/slang-options.cpp3552
-rw-r--r--source/slang/slang-parameter-binding.cpp359
-rw-r--r--source/slang/slang.cpp14
-rw-r--r--tests/bindings/hlsl-to-vulkan-combined.hlsl9
-rw-r--r--tests/bindings/hlsl-to-vulkan-combined.hlsl.expected54
-rw-r--r--tests/bindings/hlsl-to-vulkan-global.hlsl12
-rw-r--r--tests/bindings/hlsl-to-vulkan-global.hlsl.expected77
-rw-r--r--tests/bindings/hlsl-to-vulkan-shift.hlsl21
-rw-r--r--tests/bindings/hlsl-to-vulkan-shift.hlsl.expected178
18 files changed, 2779 insertions, 1791 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj
index 7e0348d73..fcc125860 100644
--- a/build/visual-studio/slang/slang.vcxproj
+++ b/build/visual-studio/slang/slang.vcxproj
@@ -344,6 +344,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
<ClInclude Include="..\..\..\source\slang\slang-emit-source-writer.h" />
<ClInclude Include="..\..\..\source\slang\slang-emit-torch.h" />
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-hlsl-to-vulkan-layout-options.h" />
<ClInclude Include="..\..\..\source\slang\slang-image-format-defs.h" />
<ClInclude Include="..\..\..\source\slang\slang-intrinsic-expand.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-addr-inst-elimination.h" />
@@ -543,6 +544,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
<ClCompile Include="..\..\..\source\slang\slang-emit-torch.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-emit.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-glsl-extension-tracker.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-hlsl-to-vulkan-layout-options.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-intrinsic-expand.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-addr-inst-elimination.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-address-analysis.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters
index cae2199b8..3ff574d10 100644
--- a/build/visual-studio/slang/slang.vcxproj.filters
+++ b/build/visual-studio/slang/slang.vcxproj.filters
@@ -120,6 +120,9 @@
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-hlsl-to-vulkan-layout-options.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-image-format-defs.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -713,6 +716,9 @@
<ClCompile Include="..\..\..\source\slang\slang-glsl-extension-tracker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-hlsl-to-vulkan-layout-options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-intrinsic-expand.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md
index e61c4245b..8b91096e7 100644
--- a/docs/command-line-slangc-reference.md
+++ b/docs/command-line-slangc-reference.md
@@ -31,6 +31,7 @@ slangc -help-style markdown -h
* [file-system-type](#file-system-type)
* [target](#target)
* [stage](#stage)
+* [vulkan-shift](#vulkan-shift)
* [capability](#capability)
* [file-extension](#file-extension)
@@ -305,6 +306,32 @@ Set the optimization level.
Remove all source file information from outputs.
+<a id="force-glsl-scalar-layout"></a>
+## -force-glsl-scalar-layout
+Force using scalar block layout for uniform and shader storage buffers in GLSL output.
+
+
+<a id="fvk-bind-globals"></a>
+## -fvk-bind-globals
+
+**-fvk-bind-globals &lt;N&gt; &lt;descriptor-set&gt;**
+
+Places the $Globals cbuffer at descriptor set &lt;descriptor-set&gt; and binding &lt;N&gt;. See HLSL global variables and Vulkan binding for explanation and examples.
+
+
+<a id="enable-effect-annotations"></a>
+## -enable-effect-annotations
+Enables support for legacy effect annotation syntax.
+
+
+<a id="fvk-b-shift"></a>
+## -fvk-b-shift, -fvk-s-shift, -fvk-t-shift, -fvk-u-shift
+
+**-vk-&lt;[vulkan-shift](#vulkan-shift)&gt;-shift &lt;N&gt; &lt;space&gt;**
+
+Shifts by N the inferred binding numbers for all resources in b-type registers of space &lt;space&gt;. Specifically, for a resouce attached with :register(bX, &lt;space&gt;) but not \[vk::binding(...)\], sets its Vulkan descriptor set to &lt;space&gt; and binding number to X + N. If you need to shift the inferred binding numbers for more than one space, provide more than one such option. If more than one such option is provided for the same space, the last one takes effect. If you need to shift the inferred binding numbers for all sets, use 'all' as &lt;space&gt;.
+
+
<a id="Downstream"></a>
# Downstream
@@ -737,6 +764,16 @@ Stage
* `mesh`
* `amplification`
+<a id="vulkan-shift"></a>
+# vulkan-shift
+
+Vulkan Shift
+
+* `b` : Vulkan Buffer resource
+* `s` : Vulkan Sampler resource
+* `t` : Vulkan Texture resource
+* `u` : Vulkan Uniform resource
+
<a id="capability"></a>
# capability
diff --git a/source/compiler-core/slang-command-line-args.h b/source/compiler-core/slang-command-line-args.h
index 31807cd48..b2bd48c61 100644
--- a/source/compiler-core/slang-command-line-args.h
+++ b/source/compiler-core/slang-command-line-args.h
@@ -109,17 +109,15 @@ struct CommandLineReader
/// Set the current index
void setIndex(Index index) { SLANG_ASSERT(index >= 0 && index <= m_args->getArgCount()); m_index = index; }
+ void init(CommandLineArgs* args, DiagnosticSink* sink) { m_args = args; m_sink = sink; m_index = 0; }
+
/// Set up reader with args
- CommandLineReader(CommandLineArgs* args, DiagnosticSink* sink):
- m_args(args),
- m_index(0),
- m_sink(sink)
- {
- }
+ CommandLineReader(CommandLineArgs* args, DiagnosticSink* sink) { init(args, sink); }
+ CommandLineReader() = default;
- DiagnosticSink* m_sink;
- CommandLineArgs* m_args;
- Index m_index;
+ DiagnosticSink* m_sink = nullptr;
+ CommandLineArgs* m_args = nullptr;
+ Index m_index = 0;
};
struct DownstreamArgs
diff --git a/source/compiler-core/slang-diagnostic-sink.h b/source/compiler-core/slang-diagnostic-sink.h
index 5131e5194..ebd43b456 100644
--- a/source/compiler-core/slang-diagnostic-sink.h
+++ b/source/compiler-core/slang-diagnostic-sink.h
@@ -255,7 +255,7 @@ public:
/// Default Ctor
DiagnosticSink():
m_sourceManager(nullptr),
- m_sourceLocationLexer (nullptr)
+ m_sourceLocationLexer(nullptr)
{
}
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 117c3f037..42fc3ca1f 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -25,6 +25,8 @@
#include "slang-syntax.h"
#include "slang-content-assist-info.h"
+#include "slang-hlsl-to-vulkan-layout-options.h"
+
#include "slang-serialize-ir-types.h"
#include "../compiler-core/slang-artifact-representation-impl.h"
@@ -1535,6 +1537,7 @@ namespace Slang
class TargetRequest : public RefObject
{
public:
+
TargetRequest(Linkage* linkage, CodeGenTarget format);
void addTargetFlags(SlangTargetFlags flags)
@@ -1575,6 +1578,10 @@ namespace Slang
return (targetFlags & SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM) != 0;
}
+ void setHLSLToVulkanLayoutOptions(HLSLToVulkanLayoutOptions* opts);
+
+ const HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions() const { return hlslToVulkanLayoutOptions; }
+
bool shouldDumpIntermediates() { return dumpIntermediates; }
void setTrackLiveness(bool enable) { enableLivenessTracking = enable; }
@@ -1611,6 +1618,8 @@ namespace Slang
bool dumpIntermediates = false;
bool forceGLSLScalarBufferLayout = false;
bool enableLivenessTracking = false;
+
+ RefPtr<HLSLToVulkanLayoutOptions> hlslToVulkanLayoutOptions; ///< Optional vulkan layout options
};
/// Are we generating code for a D3D API?
@@ -1858,7 +1867,7 @@ namespace Slang
Type* const* args,
DiagnosticSink* sink);
- /// Add a mew target and return its index.
+ /// Add a new target and return its index.
UInt addTarget(
CodeGenTarget target);
@@ -2629,6 +2638,8 @@ namespace Slang
virtual SLANG_NO_THROW void SLANG_MCALL setDiagnosticFlags(SlangDiagnosticFlags flags) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoFormat(SlangDebugInfoFormat format) SLANG_OVERRIDE;
+ void setHLSLToVulkanLayoutOptions(int targetIndex, HLSLToVulkanLayoutOptions* vulkanLayoutOptions);
+
EndToEndCompileRequest(
Session* session);
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index a35c48a6a..e8d521630 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -85,6 +85,7 @@ DIAGNOSTIC( 32, Warning, explicitStageDoesntMatchImpliedStage, "the stage spe
DIAGNOSTIC( 33, Error, stageSpecificationIgnoredBecauseNoEntryPoints, "one or more stages were specified, but no entry points were specified with '-entry'")
DIAGNOSTIC( 34, Error, stageSpecificationIgnoredBecauseBeforeAllEntryPoints, "when compiling multiple entry points, any '-stage' options must follow the '-entry' option that they apply to")
DIAGNOSTIC( 35, Error, noStageSpecifiedInPassThroughMode, "no stage was specified for entry point '$0'; when using the '-pass-through' option, stages must be fully specified on the command line")
+DIAGNOSTIC( 36, Error, expectingAnInteger, "expecting an integer value")
DIAGNOSTIC( 40, Warning, sameProfileSpecifiedMoreThanOnce, "the '$0' was specified more than once for target '$0'")
DIAGNOSTIC( 41, Error, conflictingProfilesSpecifiedForTarget, "conflicting profiles have been specified for target '$0'")
diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.cpp b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp
new file mode 100644
index 000000000..e143762a1
--- /dev/null
+++ b/source/slang/slang-hlsl-to-vulkan-layout-options.cpp
@@ -0,0 +1,132 @@
+// slang-hlsl-to-vulkan-layout-options.cpp
+
+#include "slang-hlsl-to-vulkan-layout-options.h"
+
+namespace Slang {
+
+namespace { // anonymous
+
+typedef HLSLToVulkanLayoutOptions::Kind ShiftKind;
+
+/* {b|s|t|u} */
+
+static NamesDescriptionValue s_vulkanShiftKinds[] =
+{
+ { ValueInt(ShiftKind::Buffer), "b", "Vulkan Buffer resource" },
+ { ValueInt(ShiftKind::Sampler), "s", "Vulkan Sampler resource" },
+ { ValueInt(ShiftKind::Texture), "t", "Vulkan Texture resource" },
+ { ValueInt(ShiftKind::Uniform), "u", "Vulkan Uniform resource" },
+};
+
+} // anonymous
+
+/* static */ConstArrayView<NamesDescriptionValue> HLSLToVulkanLayoutOptions::getKindInfos()
+{
+ return makeConstArrayView(s_vulkanShiftKinds);
+}
+
+HLSLToVulkanLayoutOptions::HLSLToVulkanLayoutOptions()
+{
+ for (auto& shift : m_allShifts)
+ {
+ shift = kInvalidShift;
+ }
+}
+
+
+ /// Set the the all option for the kind
+void HLSLToVulkanLayoutOptions::setAllShift(Kind kind, Index shift)
+{
+ // We try to follow the convention, of the *last* entry set is the one used.
+ // If there a "all" set, we remove everything for the kind.
+
+ // Find all the entries for the kind
+ List<Key> keys;
+ for (auto& pair : m_shifts)
+ {
+ if (pair.key.kind == kind)
+ {
+ keys.add(pair.key);
+ }
+ }
+ // Remove them all
+ for (auto& key : keys)
+ {
+ m_shifts.remove(key);
+ }
+
+ m_allShifts[Index(kind)] = shift;
+}
+
+void HLSLToVulkanLayoutOptions::setShift(Kind kind, Index set, Index shift)
+{
+ SLANG_ASSERT(shift != kInvalidShift);
+
+ Key key{ kind, set };
+ m_shifts.add(key, shift);
+}
+
+Index HLSLToVulkanLayoutOptions::getShift(Kind kind, Index set) const
+{
+ if (auto ptr = m_shifts.tryGetValue(Key{ kind, set }))
+ {
+ return *ptr;
+ }
+
+ return m_allShifts[Index(kind)];
+}
+
+bool HLSLToVulkanLayoutOptions::isDefault() const
+{
+ // If any all shift is set it's not default
+ for (auto shift : m_allShifts)
+ {
+ if (shift != kInvalidShift)
+ {
+ return false;
+ }
+ }
+
+ // If any has a non zero shift, it's not default
+ for (auto& pair : m_shifts)
+ {
+ // We need a value that is non zero...
+ if (pair.value)
+ {
+ return false;
+ }
+ }
+
+ // If either has been set it's not default
+ return m_globalsBinding < 0 && m_globalsBindingSet < 0;
+}
+
+/* static */HLSLToVulkanLayoutOptions::Kind HLSLToVulkanLayoutOptions::getKind(slang::ParameterCategory param)
+{
+ typedef slang::ParameterCategory ParameterCategory;
+
+ switch (param)
+ {
+ case ParameterCategory::Mixed:
+ {
+ // TODO(JS):
+ // Hmm, is this TextureSampler?
+ return Kind::Invalid;
+ }
+ case ParameterCategory::Uniform:
+ case ParameterCategory::ConstantBuffer:
+ {
+ return Kind::Uniform;
+ }
+ case ParameterCategory::ShaderResource: return Kind::Texture;
+ case ParameterCategory::UnorderedAccess: return Kind::Buffer;
+ case ParameterCategory::SamplerState: return Kind::Sampler;
+
+ default:
+ {
+ return Kind::Invalid;
+ }
+ }
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-hlsl-to-vulkan-layout-options.h b/source/slang/slang-hlsl-to-vulkan-layout-options.h
new file mode 100644
index 000000000..9260fe91d
--- /dev/null
+++ b/source/slang/slang-hlsl-to-vulkan-layout-options.h
@@ -0,0 +1,85 @@
+// slang-hlsl-to-vulkan-layout-options.h
+#ifndef SLANG_HLSL_TO_VULKAN_LAYOUT_OPTIONS_H
+#define SLANG_HLSL_TO_VULKAN_LAYOUT_OPTIONS_H
+
+#include "../core/slang-basic.h"
+#include "../core/slang-name-value.h"
+
+namespace Slang
+{
+
+/*
+For support features similar to described here..
+
+https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#descriptors
+
+Options that allow for infering Vulkan bindings based on HLSL register bindings
+ */
+struct HLSLToVulkanLayoutOptions : public RefObject
+{
+public:
+
+ static const Index kInvalidShift = Index(0x80000000);
+
+ // {b|s|t|u}
+ enum class Kind
+ {
+ Invalid = -1,
+
+ Buffer, ///< Buffer
+ Sampler, ///< Sampler
+ Texture, ///< Texture
+ Uniform, ///< Uniform
+
+ CountOf,
+ };
+
+ struct Key
+ {
+ typedef Key ThisType;
+
+ bool operator==(const ThisType& rhs) const { return kind == rhs.kind && set == rhs.set; }
+ bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ HashCode getHashCode() const { return combineHash(Slang::getHashCode(kind), Slang::getHashCode(set)); }
+
+ Kind kind; ///< The kind this entry is for
+ Index set; ///< The set this shift is associated with
+ };
+
+ /// Set the the all option for the kind
+ void setAllShift(Kind kind, Index shift);
+
+ /// Set the shift for kind/set
+ void setShift(Kind kind, Index set, Index shift);
+
+ /// Get the shift. Returns kInvalidShift if no shift is found
+ Index getShift(Kind kind, Index set) const;
+
+ /// Returns true if contains default information. If so it can in effect be ignored
+ bool isDefault() const;
+
+ /// True as global binds set
+ bool hasGlobalsBinding() const { return m_globalsBinding >= 0 && m_globalsBindingSet >= 0; }
+
+ /// Ctor
+ HLSLToVulkanLayoutOptions();
+
+ /// Get information about the different kinds
+ static ConstArrayView<NamesDescriptionValue> getKindInfos();
+
+ /// Given a paramCategory get the kind. Returns Kind::Invalid if not an applicable category
+ static Kind getKind(slang::ParameterCategory param);
+
+ Index m_globalsBinding = -1;
+ Index m_globalsBindingSet = -1;
+
+ Index m_allShifts[Count(Kind::CountOf)];
+
+ /// Maps a key to the amount of shift
+ Dictionary<Key, Index> m_shifts;
+};
+
+} // namespace Slang
+
+#endif
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 353c6e57f..0c788a404 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -19,6 +19,7 @@
#include "slang-repro.h"
#include "slang-serialize-ir.h"
+#include "slang-hlsl-to-vulkan-layout-options.h"
#include "../core/slang-castable.h"
#include "../core/slang-file-system.h"
@@ -81,6 +82,10 @@ enum class OptionKind
LineDirectiveMode,
Optimization,
Obfuscate,
+
+ VulkanBindShift,
+ VulkanBindGlobals,
+
GLSLForceScalarLayout,
EnableEffectAnnotations,
@@ -160,10 +165,25 @@ enum class ValueCategory
OptimizationLevel,
DebugLevel,
FileSystemType,
+ VulkanShift,
CountOf,
};
+template <typename T>
+struct GetValueCategory;
+
+#define SLANG_GET_VALUE_CATEGORY(cat, type) template <> struct GetValueCategory<type> { enum { Value = Index(ValueCategory::cat) }; };
+
+SLANG_GET_VALUE_CATEGORY(Compiler, SlangPassThrough)
+SLANG_GET_VALUE_CATEGORY(ArchiveType, SlangArchiveType)
+SLANG_GET_VALUE_CATEGORY(LineDirectiveMode, SlangLineDirectiveMode)
+SLANG_GET_VALUE_CATEGORY(FloatingPointMode, FloatingPointMode)
+SLANG_GET_VALUE_CATEGORY(FileSystemType, TypeTextUtil::FileSystemType)
+SLANG_GET_VALUE_CATEGORY(HelpStyle, CommandOptionsWriter::Style)
+SLANG_GET_VALUE_CATEGORY(OptimizationLevel, SlangOptimizationLevel)
+SLANG_GET_VALUE_CATEGORY(VulkanShift, HLSLToVulkanLayoutOptions::Kind)
+
} // anonymous
static void _addOptions(const ConstArrayView<Option>& options, CommandOptions& cmdOptions)
@@ -245,6 +265,13 @@ void initCommandOptions(CommandOptions& options)
options.addValuesWithAliases(opts.getArrayView());
}
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! vulkan-shift !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+ {
+ options.addCategory(CategoryKind::Value, "vulkan-shift", "Vulkan Shift", UserValue(ValueCategory::VulkanShift));
+ options.addValues(HLSLToVulkanLayoutOptions::getKindInfos());
+ }
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! capabilities !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
{
@@ -294,21 +321,21 @@ void initCommandOptions(CommandOptions& options)
const CommandOptions::ValuePair pairs[] =
{
{"hlsl,fx", "hlsl"},
- {"dxbc"},
+ {"dxbc", nullptr},
{"dxbc-asm", "dxbc-assembly"},
- {"dxil"},
+ {"dxil", nullptr},
{"dxil-asm", "dxil-assembly"},
- {"glsl"},
+ {"glsl", nullptr},
{"vert", "glsl (vertex)"},
{"frag", "glsl (fragment)"},
{"geom", "glsl (geoemtry)"},
{"tesc", "glsl (hull)"},
{"tese", "glsl (domain)"},
{"comp", "glsl (compute)"},
- {"slang"},
+ {"slang", nullptr},
{"spv", "SPIR-V"},
{"spv-asm", "SPIR-V assembly"},
- {"c"},
+ {"c", nullptr},
{"cpp,c++,cxx", "C++"},
{"exe", "executable"},
{"dll,so", "sharedlibrary/dll"},
@@ -418,16 +445,37 @@ void initCommandOptions(CommandOptions& options)
{ OptionKind::Optimization, "-O...", "-O<optimization-level>", "Set the optimization level."},
{ OptionKind::Obfuscate, "-obfuscate", nullptr, "Remove all source file information from outputs." },
{ OptionKind::GLSLForceScalarLayout,
- "-force-glsl-scalar-layout",
- nullptr,
+ "-force-glsl-scalar-layout", nullptr,
"Force using scalar block layout for uniform and shader storage buffers in GLSL output."},
+ { OptionKind::VulkanBindGlobals, "-fvk-bind-globals", "-fvk-bind-globals <N> <descriptor-set>",
+ "Places the $Globals cbuffer at descriptor set <descriptor-set> and binding <N>. See HLSL global variables and Vulkan binding for explanation and examples."
+ },
{ OptionKind::EnableEffectAnnotations,
- "-enable-effect-annotations",
+ "-enable-effect-annotations", nullptr,
"Enables support for legacy effect annotation syntax."},
};
_addOptions(makeConstArrayView(targetOpts), options);
+ {
+ StringBuilder names;
+ for (auto nameSlice : NameValueUtil::getNames(NameValueUtil::NameKind::All, HLSLToVulkanLayoutOptions::getKindInfos()))
+ {
+ // -fvk-{b|s|t|u}-shift
+ names << "-fvk-" << nameSlice << "-shift,";
+ }
+ // remove last ,
+ names.reduceLength(names.getLength() - 1);
+ options.add(names.getBuffer(), "-vk-<vulkan-shift>-shift <N> <space>",
+ "Shifts by N the inferred binding numbers for all resources in b-type registers of space <space>. "
+ "Specifically, for a resouce attached with :register(bX, <space>) but not [vk::binding(...)], "
+ "sets its Vulkan descriptor set to <space> and binding number to X + N. If you need to shift the "
+ "inferred binding numbers for more than one space, provide more than one such option. "
+ "If more than one such option is provided for the same space, the last one takes effect. "
+ "If you need to shift the inferred binding numbers for all sets, use 'all' as <space>.",
+ UserValue(OptionKind::VulkanBindShift));
+ }
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Downstream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
options.setCategory("Downstream");
@@ -552,13 +600,22 @@ void initCommandOptions(CommandOptions& options)
SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifact);
-struct OptionsParser
+class ReproPathVisitor : public Slang::Path::Visitor
{
- SlangSession* session = nullptr;
- SlangCompileRequest* compileRequest = nullptr;
+public:
+ virtual void accept(Slang::Path::Type type, const Slang::UnownedStringSlice& filename) SLANG_OVERRIDE
+ {
+ if (type == Path::Type::File && Path::getPathExt(filename) == "slang-repro")
+ {
+ m_filenames.add(filename);
+ }
+ }
- Slang::EndToEndCompileRequest* requestImpl = nullptr;
+ Slang::List<String> m_filenames;
+};
+struct OptionsParser
+{
// A "translation unit" represents one or more source files
// that are processed as a single entity when it comes to
// semantic checking.
@@ -603,18 +660,7 @@ struct OptionsParser
//
int translationUnitID;
};
- List<RawTranslationUnit> rawTranslationUnits;
-
- // If we already have a translation unit for Slang code, then this will give its index.
- // If not, it will be `-1`.
- int slangTranslationUnitIndex = -1;
-
- // The number of input files that have been specified
- int inputPathCount = 0;
-
- int translationUnitCount = 0;
- int currentTranslationUnitIndex= -1;
-
+
// An entry point represents a function to be checked and possibly have
// code generated in one of our translation units. An entry point
// needs to have an associated stage, which might come via the
@@ -632,22 +678,7 @@ struct OptionsParser
bool conflictingStagesSet = false;
bool redundantStageSet = false;
};
- //
- // We collect the entry points in a "raw" array so that we can
- // possibly associate them with a stage or translation unit
- // after the fact.
- //
- List<RawEntryPoint> rawEntryPoints;
-
- // In the case where we have only a single entry point,
- // the entry point and its options might be specified out
- // of order, so we will keep a single `RawEntryPoint` around
- // and use it as the target for any state-setting options
- // before the first "proper" entry point is specified.
- RawEntryPoint defaultEntryPoint;
-
- SlangCompileFlags flags = 0;
-
+
struct RawOutput
{
String path;
@@ -656,8 +687,7 @@ struct OptionsParser
int entryPointIndex = -1;
bool isWholeProgram = false;
};
- List<RawOutput> rawOutputs;
-
+
struct RawTarget
{
CodeGenTarget format = CodeGenTarget::Unknown;
@@ -673,1728 +703,1878 @@ struct OptionsParser
bool redundantProfileSet = false;
};
- List<RawTarget> rawTargets;
+
+ int addTranslationUnit(SlangSourceLanguage language, Stage impliedStage);
- RawTarget defaultTarget;
+ void addInputSlangPath(String const& path);
- int addTranslationUnit(
- SlangSourceLanguage language,
- Stage impliedStage)
- {
- auto translationUnitIndex = rawTranslationUnits.getCount();
- auto translationUnitID = compileRequest->addTranslationUnit(language, nullptr);
+ void addInputForeignShaderPath(
+ String const& path,
+ SlangSourceLanguage language,
+ Stage impliedStage);
- // As a sanity check: the API should be returning the same translation
- // unit index as we maintain internally. This invariant would only
- // be broken if we decide to support a mix of translation units specified
- // via API, and ones specified via command-line arguments.
- //
- SLANG_RELEASE_ASSERT(Index(translationUnitID) == translationUnitIndex);
+ static Profile::RawVal findGlslProfileFromPath(const String& path);
- RawTranslationUnit rawTranslationUnit;
- rawTranslationUnit.sourceLanguage = language;
- rawTranslationUnit.translationUnitID = translationUnitID;
- rawTranslationUnit.impliedStage = impliedStage;
+ static SlangSourceLanguage findSourceLanguageFromPath(const String& path, Stage& outImpliedStage);
- rawTranslationUnits.add(rawTranslationUnit);
+ SlangResult addInputPath(char const* inPath, SourceLanguage langOverride = SourceLanguage::Unknown);
- return int(translationUnitIndex);
- }
+ void addOutputPath(String const& path, CodeGenTarget impliedFormat);
- void addInputSlangPath(
- String const& path)
- {
- // All of the input .slang files will be grouped into a single logical translation unit,
- // which we create lazily when the first .slang file is encountered.
- if( slangTranslationUnitIndex == -1 )
- {
- translationUnitCount++;
- slangTranslationUnitIndex = addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG, Stage::Unknown);
- }
+ void addOutputPath(char const* inPath);
+ RawEntryPoint* getCurrentEntryPoint();
- compileRequest->addTranslationUnitSourceFile(rawTranslationUnits[slangTranslationUnitIndex].translationUnitID, path.begin());
+ void setStage(RawEntryPoint* rawEntryPoint, Stage stage);
- // Set the translation unit to be used by subsequent entry points
- currentTranslationUnitIndex = slangTranslationUnitIndex;
- }
+ RawTarget* getCurrentTarget();
+ void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion);
+ void addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom);
+
+ void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode);
+
+ SlangResult parse(
+ SlangCompileRequest* compileRequest,
+ int argc,
+ char const* const* argv);
- void addInputForeignShaderPath(
- String const& path,
- SlangSourceLanguage language,
- Stage impliedStage)
- {
- translationUnitCount++;
- currentTranslationUnitIndex = addTranslationUnit(language, impliedStage);
+ SlangResult _parse(
+ int argc,
+ char const* const* argv);
- compileRequest->addTranslationUnitSourceFile(rawTranslationUnits[currentTranslationUnitIndex].translationUnitID, path.begin());
- }
+ static bool _passThroughRequiresStage(PassThroughMode passThrough);
- static Profile::RawVal findGlslProfileFromPath(const String& path)
+
+ SlangResult _compileReproDirectory(SlangSession* session, EndToEndCompileRequest* originalRequest, const String& dir);
+
+ // Pass Severity::Disabled to allow any original severity
+ SlangResult _overrideDiagnostics(const UnownedStringSlice& identifierList, Severity originalSeverity, Severity overrideSeverity);
+
+ // Pass Severity::Disabled to allow any original severity
+ SlangResult _overrideDiagnostic(const UnownedStringSlice& identifier, Severity originalSeverity, Severity overrideSeverity);
+
+ SlangResult _dumpDiagnostics(Severity originalSeverity);
+
+ template <typename T>
+ SlangResult _getValue(const CommandLineArg& arg, const UnownedStringSlice& name, T& ioValue)
{
- struct Entry
- {
- const char* ext;
- Profile::RawVal profileId;
- };
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_getValue(ValueCategory(GetValueCategory<T>::Value), arg, name, value));
+ ioValue = T(value);
+ return SLANG_OK;
+ }
- static const Entry entries[] =
- {
- { ".frag", Profile::GLSL_Fragment },
- { ".geom", Profile::GLSL_Geometry },
- { ".tesc", Profile::GLSL_TessControl },
- { ".tese", Profile::GLSL_TessEval },
- { ".comp", Profile::GLSL_Compute }
- };
+ SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, const UnownedStringSlice& name, CommandOptions::UserValue& outValue);
+ SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, CommandOptions::UserValue& outValue);
+ SlangResult _getValue(const ConstArrayView<ValueCategory>& valueCategories, const CommandLineArg& arg, const UnownedStringSlice& name, ValueCategory& outCat, CommandOptions::UserValue& outValue);
+
+ SlangResult _expectValue(ValueCategory valueCategory, CommandOptions::UserValue& outValue);
+ SlangResult _expectInt(const CommandLineArg& arg, Int& outInt);
- for (Index i = 0; i < SLANG_COUNT_OF(entries); ++i)
- {
- const Entry& entry = entries[i];
- if (path.endsWith(entry.ext))
- {
- return entry.profileId;
- }
- }
- return Profile::Unknown;
+ template <typename T>
+ SlangResult _expectValue(T& ioValue)
+ {
+ CommandOptions::UserValue value;
+ SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory(GetValueCategory<T>::Value), value));
+ ioValue = T(value);
+ return SLANG_OK;
}
- static SlangSourceLanguage findSourceLanguageFromPath(const String& path, Stage& outImpliedStage)
+ void _appendUsageTitle(StringBuilder& out);
+ void _appendMinimalUsage(StringBuilder& out);
+ void _outputMinimalUsage();
+
+ SlangResult _parseReferenceModule(const CommandLineArg& arg);
+ SlangResult _parseReproFileSystem(const CommandLineArg& arg);
+ SlangResult _parseLoadRepro(const CommandLineArg& arg);
+ SlangResult _parseDebugInformation(const CommandLineArg& arg);
+ SlangResult _parseProfile(const CommandLineArg& arg);
+ SlangResult _parseHelp(const CommandLineArg& arg);
+
+ SlangSession* m_session = nullptr;
+ SlangCompileRequest* m_compileRequest = nullptr;
+
+ Slang::EndToEndCompileRequest* m_requestImpl = nullptr;
+
+ List<RawTarget> m_rawTargets;
+
+ RawTarget m_defaultTarget;
+
+ //
+ // We collect the entry points in a "raw" array so that we can
+ // possibly associate them with a stage or translation unit
+ // after the fact.
+ //
+ List<RawEntryPoint> m_rawEntryPoints;
+
+ // In the case where we have only a single entry point,
+ // the entry point and its options might be specified out
+ // of order, so we will keep a single `RawEntryPoint` around
+ // and use it as the target for any state-setting options
+ // before the first "proper" entry point is specified.
+ RawEntryPoint m_defaultEntryPoint;
+
+ SlangCompileFlags m_flags = 0;
+
+ RefPtr<HLSLToVulkanLayoutOptions> m_hlslToVulkanLayoutOptions;
+
+ List<RawTranslationUnit> m_rawTranslationUnits;
+
+ // If we already have a translation unit for Slang code, then this will give its index.
+ // If not, it will be `-1`.
+ int m_slangTranslationUnitIndex = -1;
+
+ // The number of input files that have been specified
+ int m_inputPathCount = 0;
+
+ int m_translationUnitCount = 0;
+ int m_currentTranslationUnitIndex = -1;
+
+ List<RawOutput> m_rawOutputs;
+
+ DiagnosticSink m_parseSink;
+ DiagnosticSink* m_sink = nullptr;
+
+ FrontEndCompileRequest* m_frontEndReq = nullptr;
+
+ SlangMatrixLayoutMode m_defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_MODE_UNKNOWN;
+
+ // The default archive type is zip
+ SlangArchiveType m_archiveType = SLANG_ARCHIVE_TYPE_ZIP;
+
+ bool m_compileStdLib = false;
+ slang::CompileStdLibFlags m_compileStdLibFlags = 0;
+ bool m_hasLoadedRepro = false;
+
+ CommandLineReader m_reader;
+
+ CommandOptionsWriter::Style m_helpStyle = CommandOptionsWriter::Style::Text;
+
+ CommandOptions* m_cmdOptions = nullptr;
+ CommandLineContext* m_cmdLineContext = nullptr;
+};
+
+int OptionsParser::addTranslationUnit(
+ SlangSourceLanguage language,
+ Stage impliedStage)
+{
+ auto translationUnitIndex = m_rawTranslationUnits.getCount();
+ auto translationUnitID = m_compileRequest->addTranslationUnit(language, nullptr);
+
+ // As a sanity check: the API should be returning the same translation
+ // unit index as we maintain internally. This invariant would only
+ // be broken if we decide to support a mix of translation units specified
+ // via API, and ones specified via command-line arguments.
+ //
+ SLANG_RELEASE_ASSERT(Index(translationUnitID) == translationUnitIndex);
+
+ RawTranslationUnit rawTranslationUnit;
+ rawTranslationUnit.sourceLanguage = language;
+ rawTranslationUnit.translationUnitID = translationUnitID;
+ rawTranslationUnit.impliedStage = impliedStage;
+
+ m_rawTranslationUnits.add(rawTranslationUnit);
+
+ return int(translationUnitIndex);
+}
+
+void OptionsParser::addInputSlangPath(
+ String const& path)
+{
+ // All of the input .slang files will be grouped into a single logical translation unit,
+ // which we create lazily when the first .slang file is encountered.
+ if (m_slangTranslationUnitIndex == -1)
{
- struct Entry
- {
- const char* ext;
- SlangSourceLanguage sourceLanguage;
- SlangStage impliedStage;
- };
+ m_translationUnitCount++;
+ m_slangTranslationUnitIndex = addTranslationUnit(SLANG_SOURCE_LANGUAGE_SLANG, Stage::Unknown);
+ }
- static const Entry entries[] =
- {
- { ".slang", SLANG_SOURCE_LANGUAGE_SLANG, SLANG_STAGE_NONE },
+ m_compileRequest->addTranslationUnitSourceFile(m_rawTranslationUnits[m_slangTranslationUnitIndex].translationUnitID, path.begin());
- { ".hlsl", SLANG_SOURCE_LANGUAGE_HLSL, SLANG_STAGE_NONE },
- { ".fx", SLANG_SOURCE_LANGUAGE_HLSL, SLANG_STAGE_NONE },
+ // Set the translation unit to be used by subsequent entry points
+ m_currentTranslationUnitIndex = m_slangTranslationUnitIndex;
+}
- { ".glsl", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_NONE },
- { ".vert", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_VERTEX },
- { ".frag", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_FRAGMENT },
- { ".geom", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_GEOMETRY },
- { ".tesc", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_HULL },
- { ".tese", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_DOMAIN },
- { ".comp", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_COMPUTE },
- { ".mesh", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_MESH },
- { ".task", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_AMPLIFICATION },
+void OptionsParser::addInputForeignShaderPath(
+ String const& path,
+ SlangSourceLanguage language,
+ Stage impliedStage)
+{
+ m_translationUnitCount++;
+ m_currentTranslationUnitIndex = addTranslationUnit(language, impliedStage);
- { ".c", SLANG_SOURCE_LANGUAGE_C, SLANG_STAGE_NONE },
- { ".cpp", SLANG_SOURCE_LANGUAGE_CPP, SLANG_STAGE_NONE },
- { ".cu", SLANG_SOURCE_LANGUAGE_CUDA, SLANG_STAGE_NONE }
+ m_compileRequest->addTranslationUnitSourceFile(m_rawTranslationUnits[m_currentTranslationUnitIndex].translationUnitID, path.begin());
+}
- };
+/* static */Profile::RawVal OptionsParser::findGlslProfileFromPath(const String& path)
+{
+ struct Entry
+ {
+ const char* ext;
+ Profile::RawVal profileId;
+ };
+
+ static const Entry entries[] =
+ {
+ { ".frag", Profile::GLSL_Fragment },
+ { ".geom", Profile::GLSL_Geometry },
+ { ".tesc", Profile::GLSL_TessControl },
+ { ".tese", Profile::GLSL_TessEval },
+ { ".comp", Profile::GLSL_Compute }
+ };
- for (Index i = 0; i < SLANG_COUNT_OF(entries); ++i)
+ for (Index i = 0; i < SLANG_COUNT_OF(entries); ++i)
+ {
+ const Entry& entry = entries[i];
+ if (path.endsWith(entry.ext))
{
- const Entry& entry = entries[i];
- if (path.endsWith(entry.ext))
- {
- outImpliedStage = Stage(entry.impliedStage);
- return entry.sourceLanguage;
- }
+ return entry.profileId;
}
- return SLANG_SOURCE_LANGUAGE_UNKNOWN;
}
+ return Profile::Unknown;
+}
- SlangResult addInputPath(
- char const* inPath,
- SourceLanguage langOverride = SourceLanguage::Unknown)
+/* static */SlangSourceLanguage OptionsParser::findSourceLanguageFromPath(const String& path, Stage& outImpliedStage)
+{
+ struct Entry
{
- inputPathCount++;
+ const char* ext;
+ SlangSourceLanguage sourceLanguage;
+ SlangStage impliedStage;
+ };
- // look at the extension on the file name to determine
- // how we should handle it.
- String path = String(inPath);
+ static const Entry entries[] =
+ {
+ { ".slang", SLANG_SOURCE_LANGUAGE_SLANG, SLANG_STAGE_NONE },
- if( path.endsWith(".slang") || langOverride == SourceLanguage::Slang)
- {
- // Plain old slang code
- addInputSlangPath(path);
- return SLANG_OK;
- }
+ { ".hlsl", SLANG_SOURCE_LANGUAGE_HLSL, SLANG_STAGE_NONE },
+ { ".fx", SLANG_SOURCE_LANGUAGE_HLSL, SLANG_STAGE_NONE },
+
+ { ".glsl", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_NONE },
+ { ".vert", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_VERTEX },
+ { ".frag", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_FRAGMENT },
+ { ".geom", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_GEOMETRY },
+ { ".tesc", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_HULL },
+ { ".tese", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_DOMAIN },
+ { ".comp", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_COMPUTE },
+ { ".mesh", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_MESH },
+ { ".task", SLANG_SOURCE_LANGUAGE_GLSL, SLANG_STAGE_AMPLIFICATION },
- Stage impliedStage = Stage::Unknown;
- SlangSourceLanguage sourceLanguage = langOverride == SourceLanguage::Unknown ? findSourceLanguageFromPath(path, impliedStage) : SlangSourceLanguage(langOverride);
+ { ".c", SLANG_SOURCE_LANGUAGE_C, SLANG_STAGE_NONE },
+ { ".cpp", SLANG_SOURCE_LANGUAGE_CPP, SLANG_STAGE_NONE },
+ { ".cu", SLANG_SOURCE_LANGUAGE_CUDA, SLANG_STAGE_NONE }
+
+ };
- if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN)
+ for (Index i = 0; i < SLANG_COUNT_OF(entries); ++i)
+ {
+ const Entry& entry = entries[i];
+ if (path.endsWith(entry.ext))
{
- requestImpl->getSink()->diagnose(SourceLoc(), Diagnostics::cannotDeduceSourceLanguage, inPath);
- return SLANG_FAIL;
+ outImpliedStage = Stage(entry.impliedStage);
+ return entry.sourceLanguage;
}
+ }
+ return SLANG_SOURCE_LANGUAGE_UNKNOWN;
+}
- addInputForeignShaderPath(path, sourceLanguage, impliedStage);
+SlangResult OptionsParser::addInputPath(char const* inPath, SourceLanguage langOverride )
+{
+ m_inputPathCount++;
+
+ // look at the extension on the file name to determine
+ // how we should handle it.
+ String path = String(inPath);
+ if (path.endsWith(".slang") || langOverride == SourceLanguage::Slang)
+ {
+ // Plain old slang code
+ addInputSlangPath(path);
return SLANG_OK;
}
- void addOutputPath(
- String const& path,
- CodeGenTarget impliedFormat)
+ Stage impliedStage = Stage::Unknown;
+ SlangSourceLanguage sourceLanguage = langOverride == SourceLanguage::Unknown ? findSourceLanguageFromPath(path, impliedStage) : SlangSourceLanguage(langOverride);
+
+ if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN)
{
- RawOutput rawOutput;
- rawOutput.path = path;
- rawOutput.impliedFormat = impliedFormat;
- rawOutputs.add(rawOutput);
+ m_requestImpl->getSink()->diagnose(SourceLoc(), Diagnostics::cannotDeduceSourceLanguage, inPath);
+ return SLANG_FAIL;
}
- void addOutputPath(char const* inPath)
- {
- String path = String(inPath);
- String ext = Path::getPathExt(path);
+ addInputForeignShaderPath(path, sourceLanguage, impliedStage);
- if (ext == toSlice("slang-module") ||
- ext == toSlice("slang-lib") ||
- ext == toSlice("dir") ||
- ext == toSlice("zip"))
- {
- // These extensions don't indicate a artifact container, just that we want to emit IR
- if (ext == toSlice("slang-module") ||
- ext == toSlice("slang-lib"))
- {
- // We want to emit IR
- requestImpl->m_emitIr = true;
- }
- else
- {
- // We want to write out in an artfact "container", that can hold multiple artifacts.
- compileRequest->setOutputContainerFormat(SLANG_CONTAINER_FORMAT_SLANG_MODULE);
- }
+ return SLANG_OK;
+}
+
+void OptionsParser::addOutputPath(
+ String const& path,
+ CodeGenTarget impliedFormat)
+{
+ RawOutput rawOutput;
+ rawOutput.path = path;
+ rawOutput.impliedFormat = impliedFormat;
+ m_rawOutputs.add(rawOutput);
+}
- requestImpl->m_containerOutputPath = path;
+void OptionsParser::addOutputPath(char const* inPath)
+{
+ String path = String(inPath);
+ String ext = Path::getPathExt(path);
+
+ if (ext == toSlice("slang-module") ||
+ ext == toSlice("slang-lib") ||
+ ext == toSlice("dir") ||
+ ext == toSlice("zip"))
+ {
+ // These extensions don't indicate a artifact container, just that we want to emit IR
+ if (ext == toSlice("slang-module") ||
+ ext == toSlice("slang-lib"))
+ {
+ // We want to emit IR
+ m_requestImpl->m_emitIr = true;
}
else
{
- const SlangCompileTarget target = TypeTextUtil::findCompileTargetFromExtension(ext.getUnownedSlice());
- // If the target is not found the value returned is Unknown. This is okay because
- // we allow an unknown-format `-o`, assuming we get a target format
- // from another argument.
- addOutputPath(path, CodeGenTarget(target));
+ // We want to write out in an artfact "container", that can hold multiple artifacts.
+ m_compileRequest->setOutputContainerFormat(SLANG_CONTAINER_FORMAT_SLANG_MODULE);
}
- }
- RawEntryPoint* getCurrentEntryPoint()
+ m_requestImpl->m_containerOutputPath = path;
+ }
+ else
{
- auto rawEntryPointCount = rawEntryPoints.getCount();
- return rawEntryPointCount ? &rawEntryPoints[rawEntryPointCount-1] : &defaultEntryPoint;
+ const SlangCompileTarget target = TypeTextUtil::findCompileTargetFromExtension(ext.getUnownedSlice());
+ // If the target is not found the value returned is Unknown. This is okay because
+ // we allow an unknown-format `-o`, assuming we get a target format
+ // from another argument.
+ addOutputPath(path, CodeGenTarget(target));
}
+}
- void setStage(RawEntryPoint* rawEntryPoint, Stage stage)
+OptionsParser::RawEntryPoint* OptionsParser::getCurrentEntryPoint()
+{
+ auto rawEntryPointCount = m_rawEntryPoints.getCount();
+ return rawEntryPointCount ? &m_rawEntryPoints[rawEntryPointCount - 1] : &m_defaultEntryPoint;
+}
+
+void OptionsParser::setStage(RawEntryPoint* rawEntryPoint, Stage stage)
+{
+ if (rawEntryPoint->stage != Stage::Unknown)
{
- if(rawEntryPoint->stage != Stage::Unknown)
+ rawEntryPoint->redundantStageSet = true;
+ if (stage != rawEntryPoint->stage)
{
- rawEntryPoint->redundantStageSet = true;
- if( stage != rawEntryPoint->stage )
- {
- rawEntryPoint->conflictingStagesSet = true;
- }
+ rawEntryPoint->conflictingStagesSet = true;
}
- rawEntryPoint->stage = stage;
}
+ rawEntryPoint->stage = stage;
+}
- RawTarget* getCurrentTarget()
- {
- auto rawTargetCount = rawTargets.getCount();
- return rawTargetCount ? &rawTargets[rawTargetCount-1] : &defaultTarget;
- }
+OptionsParser::RawTarget* OptionsParser::getCurrentTarget()
+{
+ auto rawTargetCount = m_rawTargets.getCount();
+ return rawTargetCount ? &m_rawTargets[rawTargetCount - 1] : &m_defaultTarget;
+}
- void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion)
+void OptionsParser::setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion)
+{
+ if (rawTarget->profileVersion != ProfileVersion::Unknown)
{
- if(rawTarget->profileVersion != ProfileVersion::Unknown)
- {
- rawTarget->redundantProfileSet = true;
+ rawTarget->redundantProfileSet = true;
- if(profileVersion != rawTarget->profileVersion)
- {
- rawTarget->conflictingProfilesSet = true;
- }
+ if (profileVersion != rawTarget->profileVersion)
+ {
+ rawTarget->conflictingProfilesSet = true;
}
- rawTarget->profileVersion = profileVersion;
}
+ rawTarget->profileVersion = profileVersion;
+}
- void addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom)
- {
- rawTarget->capabilityAtoms.add(atom);
- }
+void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom)
+{
+ rawTarget->capabilityAtoms.add(atom);
+}
- void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode)
- {
- rawTarget->floatingPointMode = mode;
- }
+void OptionsParser::setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode)
+{
+ rawTarget->floatingPointMode = mode;
+}
- static bool _passThroughRequiresStage(PassThroughMode passThrough)
+/* static */bool OptionsParser::_passThroughRequiresStage(PassThroughMode passThrough)
+{
+ switch (passThrough)
{
- switch (passThrough)
+ case PassThroughMode::Glslang:
+ case PassThroughMode::Dxc:
+ case PassThroughMode::Fxc:
{
- case PassThroughMode::Glslang:
- case PassThroughMode::Dxc:
- case PassThroughMode::Fxc:
- {
- return true;
- }
- default:
- {
- return false;
- }
+ return true;
+ }
+ default:
+ {
+ return false;
}
}
+}
+
+/* static */SlangResult OptionsParser::_compileReproDirectory(SlangSession* session, EndToEndCompileRequest* originalRequest, const String& dir)
+{
+ auto stdOut = originalRequest->getWriter(WriterChannel::StdOutput);
+
+ ReproPathVisitor visitor;
+ Path::find(dir, nullptr, &visitor);
- class ReproPathVisitor : public Slang::Path::Visitor
+ for (auto filename : visitor.m_filenames)
{
- public:
- virtual void accept(Slang::Path::Type type, const Slang::UnownedStringSlice& filename) SLANG_OVERRIDE
+ auto path = Path::combine(dir, filename);
+
+ ComPtr<slang::ICompileRequest> request;
+ SLANG_RETURN_ON_FAIL(session->createCompileRequest(request.writeRef()));
+
+ auto requestImpl = asInternal(request);
+
+ List<uint8_t> buffer;
+ SLANG_RETURN_ON_FAIL(ReproUtil::loadState(path, m_sink, buffer));
+
+ auto requestState = ReproUtil::getRequest(buffer);
+ MemoryOffsetBase base;
+ base.set(buffer.getBuffer(), buffer.getCount());
+
+ // If we can find a directory, that exists, we will set up a file system to load from that directory
+ ComPtr<ISlangFileSystem> fileSystem;
+ String dirPath;
+ if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(path, dirPath)))
{
- if (type == Path::Type::File && Path::getPathExt(filename) == "slang-repro")
+ SlangPathType pathType;
+ if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
{
- m_filenames.add(filename);
+ fileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath);
}
}
- Slang::List<String> m_filenames;
- };
-
- static SlangResult _compileReproDirectory(SlangSession* session, EndToEndCompileRequest* originalRequest, const String& dir, DiagnosticSink* sink)
- {
- auto stdOut = originalRequest->getWriter(WriterChannel::StdOutput);
-
- ReproPathVisitor visitor;
- Path::find(dir, nullptr, &visitor);
+ SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, requestImpl));
- for (auto filename : visitor.m_filenames)
+ if (stdOut)
{
- auto path = Path::combine(dir, filename);
+ StringBuilder buf;
+ buf << filename << "\n";
+ stdOut->write(buf.getBuffer(), buf.getLength());
+ }
- ComPtr<slang::ICompileRequest> request;
- SLANG_RETURN_ON_FAIL(session->createCompileRequest(request.writeRef()));
+ StringBuilder bufs[Index(WriterChannel::CountOf)];
+ ComPtr<ISlangWriter> writers[Index(WriterChannel::CountOf)];
+ for (Index i = 0; i < Index(WriterChannel::CountOf); ++i)
+ {
+ writers[i] = new StringWriter(&bufs[0], 0);
+ requestImpl->setWriter(WriterChannel(i), writers[i]);
+ }
- auto requestImpl = asInternal(request);
+ if (SLANG_FAILED(requestImpl->compile()))
+ {
+ const char failed[] = "FAILED!\n";
+ stdOut->write(failed, SLANG_COUNT_OF(failed) - 1);
- List<uint8_t> buffer;
- SLANG_RETURN_ON_FAIL(ReproUtil::loadState(path, sink, buffer));
+ const auto& diagnostics = bufs[Index(WriterChannel::Diagnostic)];
- auto requestState = ReproUtil::getRequest(buffer);
- MemoryOffsetBase base;
- base.set(buffer.getBuffer(), buffer.getCount());
+ stdOut->write(diagnostics.getBuffer(), diagnostics.getLength());
- // If we can find a directory, that exists, we will set up a file system to load from that directory
- ComPtr<ISlangFileSystem> fileSystem;
- String dirPath;
- if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(path, dirPath)))
- {
- SlangPathType pathType;
- if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
- {
- fileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath);
- }
- }
-
- SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, requestImpl));
+ return SLANG_FAIL;
+ }
+ }
- if (stdOut)
- {
- StringBuilder buf;
- buf << filename << "\n";
- stdOut->write(buf.getBuffer(), buf.getLength());
- }
+ if (stdOut)
+ {
+ const char end[] = "(END)\n";
+ stdOut->write(end, SLANG_COUNT_OF(end) - 1);
+ }
- StringBuilder bufs[Index(WriterChannel::CountOf)];
- ComPtr<ISlangWriter> writers[Index(WriterChannel::CountOf)];
- for (Index i = 0; i < Index(WriterChannel::CountOf); ++i)
- {
- writers[i] = new StringWriter(&bufs[0], 0);
- requestImpl->setWriter(WriterChannel(i), writers[i]);
- }
+ return SLANG_OK;
+}
- if (SLANG_FAILED(requestImpl->compile()))
- {
- const char failed[] = "FAILED!\n";
- stdOut->write(failed, SLANG_COUNT_OF(failed) - 1);
+SlangResult OptionsParser::_overrideDiagnostics(const UnownedStringSlice& identifierList, Severity originalSeverity, Severity overrideSeverity)
+{
+ List<UnownedStringSlice> slices;
+ StringUtil::split(identifierList, ',', slices);
- const auto& diagnostics = bufs[Index(WriterChannel::Diagnostic)];
+ for (const auto& slice : slices)
+ {
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostic(slice, originalSeverity, overrideSeverity));
+ }
+ return SLANG_OK;
+}
- stdOut->write(diagnostics.getBuffer(), diagnostics.getLength());
+SlangResult OptionsParser::_overrideDiagnostic(const UnownedStringSlice& identifier, Severity originalSeverity, Severity overrideSeverity)
+{
+ auto diagnosticsLookup = getDiagnosticsLookup();
- return SLANG_FAIL;
- }
- }
+ const DiagnosticInfo* diagnostic = nullptr;
+ Int diagnosticId = -1;
- if (stdOut)
+ // If it starts with a digit we assume it a number
+ if (identifier.getLength() > 0 && (CharUtil::isDigit(identifier[0]) || identifier[0] == '-'))
+ {
+ if (SLANG_FAILED(StringUtil::parseInt(identifier, diagnosticId)))
{
- const char end[] = "(END)\n";
- stdOut->write(end, SLANG_COUNT_OF(end) - 1);
+ m_sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
+ return SLANG_FAIL;
}
- return SLANG_OK;
+ // If we use numbers, we don't worry if we can't find a diagnostic
+ // and silently ignore. This was the previous behavior, and perhaps
+ // provides a way to safely disable warnings, without worrying about
+ // the version of the compiler.
+ diagnostic = diagnosticsLookup->getDiagnosticById(diagnosticId);
}
-
- // Pass Severity::Disabled to allow any original severity
- SlangResult _overrideDiagnostics(const UnownedStringSlice& identifierList, Severity originalSeverity, Severity overrideSeverity, DiagnosticSink* sink)
+ else
{
- List<UnownedStringSlice> slices;
- StringUtil::split(identifierList, ',', slices);
-
- for (const auto& slice : slices)
+ diagnostic = diagnosticsLookup->findDiagnosticByName(identifier);
+ if (!diagnostic)
{
- SLANG_RETURN_ON_FAIL(_overrideDiagnostic(slice, originalSeverity, overrideSeverity, sink));
+ m_sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
+ return SLANG_FAIL;
}
- return SLANG_OK;
+ diagnosticId = diagnostic->id;
}
- // Pass Severity::Disabled to allow any original severity
- SlangResult _overrideDiagnostic(const UnownedStringSlice& identifier, Severity originalSeverity, Severity overrideSeverity, DiagnosticSink* sink)
+ // If we are only allowing certain original severities check it's the right type
+ if (diagnostic && originalSeverity != Severity::Disable && diagnostic->severity != originalSeverity)
{
- auto diagnosticsLookup = getDiagnosticsLookup();
+ // Strictly speaking the diagnostic name is known, but it's not the right severity
+ // to be converted from, so it is an 'unknown name' in the context of severity...
+ // Or perhaps we want another diagnostic
+ m_sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
+ return SLANG_FAIL;
+ }
- const DiagnosticInfo* diagnostic = nullptr;
- Int diagnosticId = -1;
+ // Override the diagnostic severity in the sink
+ m_requestImpl->getSink()->overrideDiagnosticSeverity(int(diagnosticId), overrideSeverity, diagnostic);
- // If it starts with a digit we assume it a number
- if (identifier.getLength() > 0 && (CharUtil::isDigit(identifier[0]) || identifier[0] == '-'))
- {
- if (SLANG_FAILED(StringUtil::parseInt(identifier, diagnosticId)))
- {
- sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
- return SLANG_FAIL;
- }
+ return SLANG_OK;
+}
- // If we use numbers, we don't worry if we can't find a diagnostic
- // and silently ignore. This was the previous behavior, and perhaps
- // provides a way to safely disable warnings, without worrying about
- // the version of the compiler.
- diagnostic = diagnosticsLookup->getDiagnosticById(diagnosticId);
- }
- else
- {
- diagnostic = diagnosticsLookup->findDiagnosticByName(identifier);
- if (!diagnostic)
- {
- sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
- return SLANG_FAIL;
- }
- diagnosticId = diagnostic->id;
- }
+SlangResult OptionsParser::_dumpDiagnostics(Severity originalSeverity)
+{
+ // Get the diagnostics and dump them
+ auto diagnosticsLookup = getDiagnosticsLookup();
+
+ StringBuilder buf;
- // If we are only allowing certain original severities check it's the right type
- if (diagnostic && originalSeverity != Severity::Disable && diagnostic->severity != originalSeverity)
+ for (const auto& diagnostic : diagnosticsLookup->getDiagnostics())
+ {
+ if (originalSeverity != Severity::Disable &&
+ diagnostic->severity != originalSeverity)
{
- // Strictly speaking the diagnostic name is known, but it's not the right severity
- // to be converted from, so it is an 'unknown name' in the context of severity...
- // Or perhaps we want another diagnostic
- sink->diagnose(SourceLoc(), Diagnostics::unknownDiagnosticName, identifier);
- return SLANG_FAIL;
+ continue;
}
- // Override the diagnostic severity in the sink
- requestImpl->getSink()->overrideDiagnosticSeverity(int(diagnosticId), overrideSeverity, diagnostic);
+ buf.clear();
- return SLANG_OK;
+ buf << diagnostic->id << " : ";
+ NameConventionUtil::convert(NameStyle::Camel, UnownedStringSlice(diagnostic->name), NameConvention::LowerKabab, buf);
+ buf << "\n";
+ m_sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice());
}
- SlangResult _dumpDiagnostics(Severity originalSeverity, DiagnosticSink* sink)
- {
- // Get the diagnostics and dump them
- auto diagnosticsLookup = getDiagnosticsLookup();
+ return SLANG_OK;
+}
- StringBuilder buf;
+void OptionsParser::_appendUsageTitle(StringBuilder& out)
+{
+ out << "Usage: slangc [options...] [--] <input files>\n\n";
+}
- for (const auto& diagnostic : diagnosticsLookup->getDiagnostics())
- {
- if (originalSeverity != Severity::Disable &&
- diagnostic->severity != originalSeverity)
- {
- continue;
- }
+void OptionsParser::_outputMinimalUsage()
+{
+ // Output usage info
+ StringBuilder buf;
+ _appendMinimalUsage(buf);
- buf.clear();
+ m_sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice());
+}
- buf << diagnostic->id << " : ";
- NameConventionUtil::convert(NameStyle::Camel, UnownedStringSlice(diagnostic->name), NameConvention::LowerKabab, buf);
- buf << "\n";
- sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice());
+void OptionsParser::_appendMinimalUsage(StringBuilder& out)
+{
+ _appendUsageTitle(out);
+ out << "For help: slangc -h\n";
+}
+
+
+SlangResult OptionsParser::_getValue(ValueCategory valueCategory, const CommandLineArg& arg, const UnownedStringSlice& name, CommandOptions::UserValue& outValue)
+{
+ const auto optionIndex = m_cmdOptions->findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
+ if (optionIndex < 0)
+ {
+ const auto categoryIndex = m_cmdOptions->findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
+ SLANG_ASSERT(categoryIndex >= 0);
+ if (categoryIndex < 0)
+ {
+ return SLANG_FAIL;
}
- return SLANG_OK;
+ List<UnownedStringSlice> names;
+ m_cmdOptions->getCategoryOptionNames(categoryIndex, names);
+
+ StringBuilder buf;
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+
+ m_sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ return SLANG_FAIL;
}
- SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
- {
- auto& cmdOptions = asInternal(session)->m_commandOptions;
+ outValue = m_cmdOptions->getOptionAt(optionIndex).userValue;
+ return SLANG_OK;
+}
- const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
- if (optionIndex < 0)
- {
- const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
- SLANG_ASSERT(categoryIndex >= 0);
- if (categoryIndex < 0)
- {
- return SLANG_FAIL;
- }
+SlangResult OptionsParser::_getValue(ValueCategory valueCategory, const CommandLineArg& arg, CommandOptions::UserValue& outValue)
+{
+ return _getValue(valueCategory, arg, arg.value.getUnownedSlice(), outValue);
+}
- List<UnownedStringSlice> names;
- cmdOptions.getCategoryOptionNames(categoryIndex, names);
+SlangResult OptionsParser::_getValue(const ConstArrayView<ValueCategory>& valueCategories, const CommandLineArg& arg, const UnownedStringSlice& name, ValueCategory& outCat, CommandOptions::UserValue& outValue)
+{
+ auto& cmdOptions = asInternal(m_session)->m_commandOptions;
- StringBuilder buf;
- StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+ for (auto valueCategory : valueCategories)
+ {
+ const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
+ if (optionIndex >= 0)
+ {
+ outCat = valueCategory;
+ outValue = cmdOptions.getOptionAt(optionIndex).userValue;
+ return SLANG_OK;
+ }
+ }
- sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ List<UnownedStringSlice> names;
+ for (auto valueCategory : valueCategories)
+ {
+ const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
+ SLANG_ASSERT(categoryIndex >= 0);
+ if (categoryIndex < 0)
+ {
return SLANG_FAIL;
}
-
- outValue = cmdOptions.getOptionAt(optionIndex).userValue;
- return SLANG_OK;
+ cmdOptions.appendCategoryOptionNames(categoryIndex, names);
}
- SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ StringBuilder buf;
+ StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+
+ m_sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ return SLANG_FAIL;
+}
+
+SlangResult OptionsParser::_expectValue(ValueCategory valueCategory, CommandOptions::UserValue& outValue)
+{
+ CommandLineArg arg;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(arg));
+ SLANG_RETURN_ON_FAIL(_getValue(valueCategory, arg, outValue));
+ return SLANG_OK;
+}
+
+SlangResult OptionsParser::_expectInt(const CommandLineArg& initArg, Int& outInt)
+{
+ SLANG_UNUSED(initArg);
+
+ CommandLineArg arg;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(arg));
+
+ if (SLANG_FAILED(StringUtil::parseInt(arg.value.getUnownedSlice(), outInt)))
{
- return _getValue(valueCategory, arg, arg.value.getUnownedSlice(), sink, outValue);
+ m_sink->diagnose(arg.loc, Diagnostics::expectingAnInteger);
+ return SLANG_FAIL;
}
+ return SLANG_OK;
+}
- SlangResult _getValue(const ConstArrayView<ValueCategory>& valueCategories, const CommandLineArg& arg, const UnownedStringSlice& name, DiagnosticSink* sink, ValueCategory& outCat, CommandOptions::UserValue& outValue)
- {
- auto& cmdOptions = asInternal(session)->m_commandOptions;
+SlangResult OptionsParser::_parseReferenceModule(const CommandLineArg& arg)
+{
+ SLANG_UNUSED(arg);
- for (auto valueCategory : valueCategories)
- {
- const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), name);
- if (optionIndex >= 0)
- {
- outCat = valueCategory;
- outValue = cmdOptions.getOptionAt(optionIndex).userValue;
- return SLANG_OK;
- }
- }
+ CommandLineArg referenceModuleName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(referenceModuleName));
- List<UnownedStringSlice> names;
- for (auto valueCategory : valueCategories)
- {
- const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory));
- SLANG_ASSERT(categoryIndex >= 0);
- if (categoryIndex < 0)
- {
- return SLANG_FAIL;
- }
- cmdOptions.appendCategoryOptionNames(categoryIndex, names);
- }
+ const auto path = referenceModuleName.value;
- StringBuilder buf;
- StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf);
+ auto desc = ArtifactDescUtil::getDescFromPath(path.getUnownedSlice());
- sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf);
+ if (desc.kind == ArtifactKind::Unknown)
+ {
+ m_sink->diagnose(referenceModuleName.loc, Diagnostics::unknownLibraryKind, Path::getPathExt(path));
return SLANG_FAIL;
}
- SlangResult _expectValue(ValueCategory valueCategory, CommandLineReader& reader, DiagnosticSink* sink, CommandOptions::UserValue& outValue)
+ // If it's a GPU binary, then we'll assume it's a library
+ if (ArtifactDescUtil::isGpuUsable(desc))
{
- CommandLineArg arg;
- SLANG_RETURN_ON_FAIL(reader.expectArg(arg));
- SLANG_RETURN_ON_FAIL(_getValue(valueCategory, arg, sink, outValue));
- return SLANG_OK;
+ desc.kind = ArtifactKind::Library;
}
- void _appendUsageTitle(StringBuilder& out)
+ // If its a zip we'll *assume* its a zip holding compilation results
+ if (desc.kind == ArtifactKind::Zip)
{
- out << "Usage: slangc [options...] [--] <input files>\n\n";
+ desc.payload = ArtifactPayload::CompileResults;
}
- void _appendMinimalUsage(StringBuilder& out)
+
+ if (!ArtifactDescUtil::isLinkable(desc))
{
- _appendUsageTitle(out);
- out << "For help: slangc -h\n";
+ m_sink->diagnose(referenceModuleName.loc, Diagnostics::kindNotLinkable, Path::getPathExt(path));
+ return SLANG_FAIL;
}
- void _outputMinimalUsage(DiagnosticSink* sink)
+
+ const String name = ArtifactDescUtil::getBaseNameFromPath(desc, path.getUnownedSlice());
+
+ // Create the artifact
+ auto artifact = Artifact::create(desc, name.getUnownedSlice());
+
+ // There is a problem here if I want to reference a library that is a 'system' library or is not directly a file
+ // In that case the path shouldn't be set and the name should completely define the library.
+ // Seeing as on all targets the baseName doesn't have an extension, and all library types do
+ // if the name doesn't have an extension we can assume there is no path to it.
+
+ ComPtr<IOSFileArtifactRepresentation> fileRep;
+ if (Path::getPathExt(path).getLength() <= 0)
{
- // Output usage info
- StringBuilder buf;
- _appendMinimalUsage(buf);
+ // If there is no extension *assume* it is the name of a system level library
+ fileRep = new OSFileArtifactRepresentation(IOSFileArtifactRepresentation::Kind::NameOnly, path.getUnownedSlice(), nullptr);
+ }
+ else
+ {
+ fileRep = new OSFileArtifactRepresentation(IOSFileArtifactRepresentation::Kind::Reference, path.getUnownedSlice(), nullptr);
+ if (!fileRep->exists())
+ {
+ m_sink->diagnose(referenceModuleName.loc, Diagnostics::libraryDoesNotExist, path);
+ return SLANG_FAIL;
+ }
+ }
+ artifact->addRepresentation(fileRep);
+
+ SLANG_RETURN_ON_FAIL(_addLibraryReference(m_requestImpl, artifact));
+ return SLANG_OK;
+}
+
+SlangResult OptionsParser::_parseReproFileSystem(const CommandLineArg& arg)
+{
+ SLANG_UNUSED(arg);
- sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice());
+ CommandLineArg reproName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(reproName));
+
+ List<uint8_t> buffer;
+ {
+ const Result res = ReproUtil::loadState(reproName.value, m_sink, buffer);
+ if (SLANG_FAILED(res))
+ {
+ m_sink->diagnose(reproName.loc, Diagnostics::unableToReadFile, reproName.value);
+ return res;
+ }
}
- SlangResult parse(
- int argc,
- char const* const* argv)
- {
-
- // Copy some state out of the current request, in case we've been called
- // after some other initialization has been performed.
- flags = requestImpl->getFrontEndReq()->compileFlags;
-
- DiagnosticSink* requestSink = requestImpl->getSink();
-
- CommandLineContext* cmdLineContext = requestImpl->getLinkage()->m_downstreamArgs.getContext();
-
- // Why create a new DiagnosticSink?
- // We *don't* want the lexer that comes as default (it's for Slang source!)
- // We may want to set flags that are different
- // We will need to use a new sourceManager that will just last for this parse and will map locs to
- // source lines.
- //
- // The *problem* is that we still need to communicate to the requestSink in some suitable way.
- //
- // 1) We could have some kind of scoping mechanism (and only one sink)
- // 2) We could have a 'parent' diagnostic sink, that if we set we route output too
- // 3) We use something like the ISlangWriter to always be the thing output too (this has problems because
- // some code assumes the diagnostics are accessible as a string)
- //
- // The solution used here is to have DiagnosticsSink have a 'parent' that also gets diagnostics reported to.
-
- DiagnosticSink parseSink(cmdLineContext->getSourceManager(), nullptr);
-
+ auto requestState = ReproUtil::getRequest(buffer);
+ MemoryOffsetBase base;
+ base.set(buffer.getBuffer(), buffer.getCount());
+
+ // If we can find a directory, that exists, we will set up a file system to load from that directory
+ ComPtr<ISlangFileSystem> dirFileSystem;
+ String dirPath;
+ if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(reproName.value, dirPath)))
+ {
+ SlangPathType pathType;
+ if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
{
- parseSink.setFlags(requestSink->getFlags());
- // Allow HumaneLoc - it won't display much for command line parsing - just (1):
- // Leaving allows for diagnostics to be compatible with other Slang diagnostic parsing.
- //parseSink.resetFlag(DiagnosticSink::Flag::HumaneLoc);
- parseSink.setFlag(DiagnosticSink::Flag::SourceLocationLine);
+ dirFileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath, true);
}
+ }
+
+ ComPtr<ISlangFileSystemExt> fileSystem;
+ SLANG_RETURN_ON_FAIL(ReproUtil::loadFileSystem(base, requestState, dirFileSystem, fileSystem));
+
+ auto cacheFileSystem = as<CacheFileSystem>(fileSystem);
+ SLANG_ASSERT(cacheFileSystem);
+
+ // I might want to make the dir file system the fallback file system...
+ cacheFileSystem->setInnerFileSystem(dirFileSystem, cacheFileSystem->getUniqueIdentityMode(), cacheFileSystem->getPathStyle());
+
+ // Set as the file system
+ m_compileRequest->setFileSystem(fileSystem);
+ return SLANG_OK;
+}
- // All diagnostics will also be sent to requestSink
- parseSink.setParentSink(requestSink);
+SlangResult OptionsParser::_parseHelp(const CommandLineArg& arg)
+{
+ SLANG_UNUSED(arg);
- DiagnosticSink* sink = &parseSink;
+ Index categoryIndex = -1;
- // Set up the args
- CommandLineArgs args(cmdLineContext);
- // Converts input args into args in 'args'.
- // Doing so will allocate some SourceLoc space from the CommandLineContext.
- args.setArgs(argv, argc);
+ if (m_reader.hasArg())
+ {
+ auto catArg = m_reader.getArgAndAdvance();
+ categoryIndex = m_cmdOptions->findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice());
+ if (categoryIndex < 0)
{
- auto linkage = requestImpl->getLinkage();
- // Before we do anything else lets strip out all of the downstream arguments.
- SLANG_RETURN_ON_FAIL(linkage->m_downstreamArgs.stripDownstreamArgs(args, 0, sink));
+ m_sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory);
+ return SLANG_FAIL;
}
+ }
- CommandLineReader reader(&args, sink);
+ CommandOptionsWriter::Options writerOptions;
+ writerOptions.style = m_helpStyle;
- SlangMatrixLayoutMode defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_MODE_UNKNOWN;
+ auto writer = CommandOptionsWriter::create(writerOptions);
- // The default archive type is zip
- SlangArchiveType archiveType = SLANG_ARCHIVE_TYPE_ZIP;
+ auto& buf = writer->getBuilder();
+
+ if (categoryIndex < 0)
+ {
+ // If it's the text style we can inject usage at the top
+ if (m_helpStyle == CommandOptionsWriter::Style::Text)
+ {
+ _appendUsageTitle(buf);
+ }
+ else
+ {
+ // NOTE! We need this preamble because if we have links,
+ // we have to make sure the first thing in markdown *isn't* <>
+
+ buf << "# Slang Command Line Options\n\n";
+ buf << "*Usage:*\n";
+ buf << "```\n";
+ buf << "slangc [options...] [--] <input files>\n\n";
+ buf << "# For help\n";
+ buf << "slangc -h\n\n";
+ buf << "# To generate this file\n";
+ buf << "slangc -help-style markdown -h\n";
+ buf << "```\n";
+ }
+
+ writer->appendDescription(m_cmdOptions);
+ }
+ else
+ {
+ writer->appendDescriptionForCategory(m_cmdOptions, categoryIndex);
+ }
+
+ m_sink->diagnoseRaw(Severity::Note, buf.getBuffer());
- bool compileStdLib = false;
- slang::CompileStdLibFlags compileStdLibFlags = 0;
- bool hasLoadedRepro = false;
+ return SLANG_OK;
+}
- // Get the options on the session
- CommandOptions& options = asInternal(session)->m_commandOptions;
- CommandOptionsWriter::Style helpStyle = CommandOptionsWriter::Style::Text;
+SlangResult OptionsParser::_parseLoadRepro(const CommandLineArg& arg)
+{
+ SLANG_UNUSED(arg);
- auto frontEndReq = requestImpl->getFrontEndReq();
+ CommandLineArg reproName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(reproName));
- while (reader.hasArg())
+ List<uint8_t> buffer;
+ {
+ const Result res = ReproUtil::loadState(reproName.value, m_sink, buffer);
+ if (SLANG_FAILED(res))
{
- auto arg = reader.getArgAndAdvance();
- const auto& argValue = arg.value;
+ m_sink->diagnose(reproName.loc, Diagnostics::unableToReadFile, reproName.value);
+ return res;
+ }
+ }
- // If it's not an option we assume it's a path
- if (argValue[0] != '-')
- {
- SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer()));
- continue;
- }
+ auto requestState = ReproUtil::getRequest(buffer);
+ MemoryOffsetBase base;
+ base.set(buffer.getBuffer(), buffer.getCount());
- const Index optionIndex = options.findOptionByName(argValue.getUnownedSlice());
+ // If we can find a directory, that exists, we will set up a file system to load from that directory
+ ComPtr<ISlangFileSystem> fileSystem;
+ String dirPath;
+ if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(reproName.value, dirPath)))
+ {
+ SlangPathType pathType;
+ if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
+ {
+ fileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath);
+ }
+ }
- if (optionIndex < 0)
- {
- sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
- _outputMinimalUsage(sink);
- return SLANG_FAIL;
- }
+ SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, m_requestImpl));
- const auto optionKind = OptionKind(options.getOptionAt(optionIndex).userValue);
+ m_hasLoadedRepro = true;
+ return SLANG_OK;
+}
- switch (optionKind)
- {
- case OptionKind::NoMangle: flags |= SLANG_COMPILE_FLAG_NO_MANGLING; break;
- case OptionKind::EmitIr: requestImpl->m_emitIr = true; break;
- case OptionKind::LoadStdLib:
- {
- CommandLineArg fileName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
-
- // Load the file
- ScopedAllocation contents;
- SLANG_RETURN_ON_FAIL(File::readAllBytes(fileName.value, contents));
- SLANG_RETURN_ON_FAIL(session->loadStdLib(contents.getData(), contents.getSizeInBytes()));
- break;
- }
- case OptionKind::CompileStdLib: compileStdLib = true; break;
- case OptionKind::ArchiveType:
- {
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::ArchiveType, reader, sink, value));
- archiveType = SlangArchiveType(value);
- break;
- }
- case OptionKind::SaveStdLib:
- {
- CommandLineArg fileName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
+SlangResult OptionsParser::_parseDebugInformation(const CommandLineArg& arg)
+{
+ auto name = arg.value.getUnownedSlice().tail(2);
- ComPtr<ISlangBlob> blob;
+ // Note: unlike with `-O` above, we have to consider that other
+ // options might have names that start with `-g` and so cannot
+ // just detect it as a prefix.
+ if (name.getLength() == 0)
+ {
+ // The default is standard
+ m_compileRequest->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_STANDARD);
+ }
+ else
+ {
+ CommandOptions::UserValue value;
+ ValueCategory valueCat;
+ ValueCategory valueCats[] = { ValueCategory::DebugLevel, ValueCategory::DebugInfoFormat };
+ SLANG_RETURN_ON_FAIL(_getValue(makeConstArrayView(valueCats), arg, name, valueCat, value));
- SLANG_RETURN_ON_FAIL(session->saveStdLib(archiveType, blob.writeRef()));
- SLANG_RETURN_ON_FAIL(File::writeAllBytes(fileName.value, blob->getBufferPointer(), blob->getBufferSize()));
- break;
- }
- case OptionKind::SaveStdLibBinSource:
- {
- CommandLineArg fileName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(fileName));
+ if (valueCat == ValueCategory::DebugLevel)
+ {
+ const auto level = (SlangDebugInfoLevel)value;
+ m_compileRequest->setDebugInfoLevel(level);
+ }
+ else
+ {
+ const auto debugFormat = (SlangDebugInfoFormat)value;
+ m_compileRequest->setDebugInfoFormat(debugFormat);
+ }
+ }
+ return SLANG_OK;
+}
- ComPtr<ISlangBlob> blob;
- SLANG_RETURN_ON_FAIL(session->saveStdLib(archiveType, blob.writeRef()));
+SlangResult OptionsParser::_parseProfile(const CommandLineArg& arg)
+{
+ SLANG_UNUSED(arg);
- StringBuilder builder;
- StringWriter writer(&builder, 0);
+ // A "profile" can specify both a general capability level for
+ // a target, and also (as a legacy/compatibility feature) a
+ // specific stage to use for an entry point.
- SLANG_RETURN_ON_FAIL(HexDumpUtil::dumpSourceBytes((const uint8_t*)blob->getBufferPointer(), blob->getBufferSize(), 16, &writer));
+ CommandLineArg operand;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(operand));
- File::writeAllText(fileName.value, builder);
- break;
- }
- case OptionKind::NoCodeGen: flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; break;
- case OptionKind::DumpIntermediates: compileRequest->setDumpIntermediates(true); break;
- case OptionKind::DumpIrIds:
- {
- frontEndReq->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds;
- break;
- }
- case OptionKind::DumpIntermediatePrefix:
- {
- CommandLineArg prefix;
- SLANG_RETURN_ON_FAIL(reader.expectArg(prefix));
- requestImpl->m_dumpIntermediatePrefix = prefix.value;
- break;
- }
- case OptionKind::OutputIncludes: frontEndReq->outputIncludes = true; break;
- case OptionKind::DumpIr: frontEndReq->shouldDumpIR = true; break;
- case OptionKind::PreprocessorOutput: frontEndReq->outputPreprocessor = true; break;
- case OptionKind::DumpAst: frontEndReq->shouldDumpAST = true; break;
- case OptionKind::Doc:
- {
- // If compiling stdlib is enabled, will write out documentation
- compileStdLibFlags |= slang::CompileStdLibFlag::WriteDocumentation;
+ // A a convenience, the `-profile` option supports an operand that consists
+ // of multiple tokens separated with `+`. The eventual goal is that each
+ // of these tokens will represent a capability that should be assumed to
+ // be present on the target.
+ //
+ List<UnownedStringSlice> slices;
+ StringUtil::split(operand.value.getUnownedSlice(), '+', slices);
+ Index sliceCount = slices.getCount();
- // Enable writing out documentation on the req
- frontEndReq->shouldDocument = true;
- break;
- }
- case OptionKind::DumpRepro:
- {
- CommandLineArg dumpRepro;
- SLANG_RETURN_ON_FAIL(reader.expectArg(dumpRepro));
- requestImpl->m_dumpRepro = dumpRepro.value;
- compileRequest->enableReproCapture();
- break;
- }
- case OptionKind::DumpReproOnError: requestImpl->m_dumpReproOnError = true; break;
- case OptionKind::ExtractRepro:
- {
- CommandLineArg reproName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
+ // For now, we will require that the *first* capability in the list is
+ // special, and represents the traditional `Profile` to compile for in
+ // the existing Slang model.
+ //
+ UnownedStringSlice profileName = sliceCount >= 1 ? slices[0] : UnownedTerminatedStringSlice("");
- {
- const Result res = ReproUtil::extractFilesToDirectory(reproName.value, sink);
- if (SLANG_FAILED(res))
- {
- sink->diagnose(reproName.loc, Diagnostics::unableExtractReproToDirectory, reproName.value);
- return res;
- }
- }
- break;
- }
- case OptionKind::ModuleName:
- {
- CommandLineArg moduleName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(moduleName));
+ SlangProfileID profileID = SlangProfileID(Slang::Profile::lookUp(profileName).raw);
+ if (profileID == SLANG_PROFILE_UNKNOWN)
+ {
+ m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, profileName);
+ return SLANG_FAIL;
+ }
+ else
+ {
+ auto profile = Profile(profileID);
- compileRequest->setDefaultModuleName(moduleName.value.getBuffer());
- break;
- }
- case OptionKind::LoadRepro:
- {
- CommandLineArg reproName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
+ setProfileVersion(getCurrentTarget(), profile.getVersion());
- List<uint8_t> buffer;
- {
- const Result res = ReproUtil::loadState(reproName.value, sink, buffer);
- if (SLANG_FAILED(res))
- {
- sink->diagnose(reproName.loc, Diagnostics::unableToReadFile, reproName.value);
- return res;
- }
- }
+ // A `-profile` option that also specifies a stage (e.g., `-profile vs_5_0`)
+ // should be treated like a composite (e.g., `-profile sm_5_0 -stage vertex`)
+ auto stage = profile.getStage();
+ if (stage != Stage::Unknown)
+ {
+ setStage(getCurrentEntryPoint(), stage);
+ }
+ }
- auto requestState = ReproUtil::getRequest(buffer);
- MemoryOffsetBase base;
- base.set(buffer.getBuffer(), buffer.getCount());
+ // Any additional capability tokens will be assumed to represent `CapabilityAtom`s.
+ // Those atoms will need to be added to the supported capabilities of the target.
+ //
+ for (Index i = 1; i < sliceCount; ++i)
+ {
+ UnownedStringSlice atomName = slices[i];
+ CapabilityAtom atom = findCapabilityAtom(atomName);
+ if (atom == CapabilityAtom::Invalid)
+ {
+ m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
+ return SLANG_FAIL;
+ }
- // If we can find a directory, that exists, we will set up a file system to load from that directory
- ComPtr<ISlangFileSystem> fileSystem;
- String dirPath;
- if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(reproName.value, dirPath)))
- {
- SlangPathType pathType;
- if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
- {
- fileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath);
- }
- }
+ addCapabilityAtom(getCurrentTarget(), atom);
+ }
- SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, requestImpl));
+ return SLANG_OK;
+}
- hasLoadedRepro = true;
- break;
- }
- case OptionKind::LoadReproDirectory:
- {
- CommandLineArg reproDirectory;
- SLANG_RETURN_ON_FAIL(reader.expectArg(reproDirectory));
+SlangResult OptionsParser::_parse(
+ int argc,
+ char const* const* argv)
+{
+ // Copy some state out of the current request, in case we've been called
+ // after some other initialization has been performed.
+ m_flags = m_requestImpl->getFrontEndReq()->compileFlags;
- SLANG_RETURN_ON_FAIL(_compileReproDirectory(session, requestImpl, reproDirectory.value, sink));
- break;
- }
- case OptionKind::ReproFileSystem:
- {
- CommandLineArg reproName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(reproName));
+ // Set up the args
+ CommandLineArgs args(m_cmdLineContext);
+ // Converts input args into args in 'args'.
+ // Doing so will allocate some SourceLoc space from the CommandLineContext.
+ args.setArgs(argv, argc);
- List<uint8_t> buffer;
- {
- const Result res = ReproUtil::loadState(reproName.value, sink, buffer);
- if (SLANG_FAILED(res))
- {
- sink->diagnose(reproName.loc, Diagnostics::unableToReadFile, reproName.value);
- return res;
- }
- }
+ {
+ auto linkage = m_requestImpl->getLinkage();
+ // Before we do anything else lets strip out all of the downstream arguments.
+ SLANG_RETURN_ON_FAIL(linkage->m_downstreamArgs.stripDownstreamArgs(args, 0, m_sink));
+ }
- auto requestState = ReproUtil::getRequest(buffer);
- MemoryOffsetBase base;
- base.set(buffer.getBuffer(), buffer.getCount());
+ m_reader.init(&args, m_sink);
- // If we can find a directory, that exists, we will set up a file system to load from that directory
- ComPtr<ISlangFileSystem> dirFileSystem;
- String dirPath;
- if (SLANG_SUCCEEDED(ReproUtil::calcDirectoryPathFromFilename(reproName.value, dirPath)))
- {
- SlangPathType pathType;
- if (SLANG_SUCCEEDED(Path::getPathType(dirPath, &pathType)) && pathType == SLANG_PATH_TYPE_DIRECTORY)
- {
- dirFileSystem = new RelativeFileSystem(OSFileSystem::getExtSingleton(), dirPath, true);
- }
- }
+ while (m_reader.hasArg())
+ {
+ auto arg = m_reader.getArgAndAdvance();
+ const auto& argValue = arg.value;
- ComPtr<ISlangFileSystemExt> fileSystem;
- SLANG_RETURN_ON_FAIL(ReproUtil::loadFileSystem(base, requestState, dirFileSystem, fileSystem));
+ // If it's not an option we assume it's a path
+ if (argValue[0] != '-')
+ {
+ SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer()));
+ continue;
+ }
- auto cacheFileSystem = as<CacheFileSystem>(fileSystem);
- SLANG_ASSERT(cacheFileSystem);
+ const Index optionIndex = m_cmdOptions->findOptionByName(argValue.getUnownedSlice());
- // I might want to make the dir file system the fallback file system...
- cacheFileSystem->setInnerFileSystem(dirFileSystem, cacheFileSystem->getUniqueIdentityMode(), cacheFileSystem->getPathStyle());
+ if (optionIndex < 0)
+ {
+ m_sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
+ _outputMinimalUsage();
+ return SLANG_FAIL;
+ }
- // Set as the file system
- compileRequest->setFileSystem(fileSystem);
- break;
- }
- case OptionKind::SerialIr: frontEndReq->useSerialIRBottleneck = true; break;
- case OptionKind::DisableSpecialization: requestImpl->disableSpecialization = true; break;
- case OptionKind::DisableDynamicDispatch: requestImpl->disableDynamicDispatch = true; break;
- case OptionKind::TrackLiveness: requestImpl->setTrackLiveness(true); break;
- case OptionKind::VerbosePaths: requestImpl->getSink()->setFlag(DiagnosticSink::Flag::VerbosePath); break;
- case OptionKind::DumpWarningDiagnostics: _dumpDiagnostics(Severity::Warning, sink); break;
- case OptionKind::WarningsAsErrors:
- {
- CommandLineArg operand;
- SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
+ const auto optionKind = OptionKind(m_cmdOptions->getOptionAt(optionIndex).userValue);
- if (operand.value == "all")
- {
- // TODO(JS):
- // Perhaps there needs to be a way to disable this selectively.
- requestImpl->getSink()->setFlag(DiagnosticSink::Flag::TreatWarningsAsErrors);
- }
- else
- {
- SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Error, sink));
- }
- break;
- }
- case OptionKind::DisableWarnings:
- {
- CommandLineArg operand;
- SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
- SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Disable, sink));
- break;
- }
- case OptionKind::DisableWarning:
- {
- // 5 because -Wno-
- auto name = argValue.getUnownedSlice().tail(5);
- SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Disable, sink));
- break;
- }
- case OptionKind::EnableWarning:
- {
- // 2 because -W
- auto name = argValue.getUnownedSlice().tail(2);
- // Enable the warning
- SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning, sink));
- break;
- }
- case OptionKind::VerifyDebugSerialIr: frontEndReq->verifyDebugSerialization = true; break;
- case OptionKind::ValidateIr: frontEndReq->shouldValidateIR = true; break;
- case OptionKind::SkipCodeGen: requestImpl->m_shouldSkipCodegen = true; break;
- case OptionKind::ParameterBlocksUseRegisterSpaces:
- {
- getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES;
- break;
- }
- case OptionKind::IrCompression:
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+ switch (optionKind)
+ {
+ case OptionKind::NoMangle: m_flags |= SLANG_COMPILE_FLAG_NO_MANGLING; break;
+ case OptionKind::EmitIr: m_requestImpl->m_emitIr = true; break;
+ case OptionKind::LoadStdLib:
+ {
+ CommandLineArg fileName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(fileName));
- SLANG_RETURN_ON_FAIL(SerialParseUtil::parseCompressionType(name.value.getUnownedSlice(), requestImpl->getLinkage()->serialCompressionType));
- break;
- }
- case OptionKind::Target:
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+ // Load the file
+ ScopedAllocation contents;
+ SLANG_RETURN_ON_FAIL(File::readAllBytes(fileName.value, contents));
+ SLANG_RETURN_ON_FAIL(m_session->loadStdLib(contents.getData(), contents.getSizeInBytes()));
+ break;
+ }
+ case OptionKind::CompileStdLib: m_compileStdLib = true; break;
+ case OptionKind::ArchiveType: SLANG_RETURN_ON_FAIL(_expectValue(m_archiveType)); break;
+ case OptionKind::SaveStdLib:
+ {
+ CommandLineArg fileName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(fileName));
- const CodeGenTarget format = (CodeGenTarget)TypeTextUtil::findCompileTargetFromName(name.value.getUnownedSlice());
+ ComPtr<ISlangBlob> blob;
- if (format == CodeGenTarget::Unknown)
- {
- sink->diagnose(name.loc, Diagnostics::unknownCodeGenerationTarget, name.value);
- return SLANG_FAIL;
- }
+ SLANG_RETURN_ON_FAIL(m_session->saveStdLib(m_archiveType, blob.writeRef()));
+ SLANG_RETURN_ON_FAIL(File::writeAllBytes(fileName.value, blob->getBufferPointer(), blob->getBufferSize()));
+ break;
+ }
+ case OptionKind::SaveStdLibBinSource:
+ {
+ CommandLineArg fileName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(fileName));
- RawTarget rawTarget;
- rawTarget.format = CodeGenTarget(format);
+ ComPtr<ISlangBlob> blob;
- rawTargets.add(rawTarget);
- break;
- }
- case OptionKind::Profile:
- {
- // A "profile" can specify both a general capability level for
- // a target, and also (as a legacy/compatibility feature) a
- // specific stage to use for an entry point.
-
- CommandLineArg operand;
- SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
-
- // A a convenience, the `-profile` option supports an operand that consists
- // of multiple tokens separated with `+`. The eventual goal is that each
- // of these tokens will represent a capability that should be assumed to
- // be present on the target.
- //
- List<UnownedStringSlice> slices;
- StringUtil::split(operand.value.getUnownedSlice(), '+', slices);
- Index sliceCount = slices.getCount();
-
- // For now, we will require that the *first* capability in the list is
- // special, and represents the traditional `Profile` to compile for in
- // the existing Slang model.
- //
- UnownedStringSlice profileName = sliceCount >= 1 ? slices[0] : UnownedTerminatedStringSlice("");
-
- SlangProfileID profileID = SlangProfileID(Slang::Profile::lookUp(profileName).raw);
- if( profileID == SLANG_PROFILE_UNKNOWN )
- {
- sink->diagnose(operand.loc, Diagnostics::unknownProfile, profileName);
- return SLANG_FAIL;
- }
- else
- {
- auto profile = Profile(profileID);
+ SLANG_RETURN_ON_FAIL(m_session->saveStdLib(m_archiveType, blob.writeRef()));
- setProfileVersion(getCurrentTarget(), profile.getVersion());
+ StringBuilder builder;
+ StringWriter writer(&builder, 0);
- // A `-profile` option that also specifies a stage (e.g., `-profile vs_5_0`)
- // should be treated like a composite (e.g., `-profile sm_5_0 -stage vertex`)
- auto stage = profile.getStage();
- if(stage != Stage::Unknown)
- {
- setStage(getCurrentEntryPoint(), stage);
- }
- }
+ SLANG_RETURN_ON_FAIL(HexDumpUtil::dumpSourceBytes((const uint8_t*)blob->getBufferPointer(), blob->getBufferSize(), 16, &writer));
- // Any additional capability tokens will be assumed to represent `CapabilityAtom`s.
- // Those atoms will need to be added to the supported capabilities of the target.
- //
- for(Index i = 1; i < sliceCount; ++i)
- {
- UnownedStringSlice atomName = slices[i];
- CapabilityAtom atom = findCapabilityAtom(atomName);
- if( atom == CapabilityAtom::Invalid )
- {
- sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
- return SLANG_FAIL;
- }
-
- addCapabilityAtom(getCurrentTarget(), atom);
- }
+ File::writeAllText(fileName.value, builder);
+ break;
+ }
+ case OptionKind::NoCodeGen: m_flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; break;
+ case OptionKind::DumpIntermediates: m_compileRequest->setDumpIntermediates(true); break;
+ case OptionKind::DumpIrIds:
+ {
+ m_frontEndReq->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds;
+ break;
+ }
+ case OptionKind::DumpIntermediatePrefix:
+ {
+ CommandLineArg prefix;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(prefix));
+ m_requestImpl->m_dumpIntermediatePrefix = prefix.value;
+ break;
+ }
+ case OptionKind::OutputIncludes: m_frontEndReq->outputIncludes = true; break;
+ case OptionKind::DumpIr: m_frontEndReq->shouldDumpIR = true; break;
+ case OptionKind::PreprocessorOutput: m_frontEndReq->outputPreprocessor = true; break;
+ case OptionKind::DumpAst: m_frontEndReq->shouldDumpAST = true; break;
+ case OptionKind::Doc:
+ {
+ // If compiling stdlib is enabled, will write out documentation
+ m_compileStdLibFlags |= slang::CompileStdLibFlag::WriteDocumentation;
+
+ // Enable writing out documentation on the req
+ m_frontEndReq->shouldDocument = true;
+ break;
+ }
+ case OptionKind::DumpRepro:
+ {
+ CommandLineArg dumpRepro;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(dumpRepro));
+ m_requestImpl->m_dumpRepro = dumpRepro.value;
+ m_compileRequest->enableReproCapture();
+ break;
+ }
+ case OptionKind::DumpReproOnError: m_requestImpl->m_dumpReproOnError = true; break;
+ case OptionKind::ExtractRepro:
+ {
+ CommandLineArg reproName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(reproName));
- break;
- }
- case OptionKind::Capability:
{
- // The `-capability` option is similar to `-profile` but does not set the actual profile
- // for a target (it just adds capabilities).
- //
- // TODO: Once profiles are treated as capabilities themselves, it might be possible
- // to treat `-profile` and `-capability` as aliases, although there might still be
- // value in only allowing a single `-profile` option per target while still allowing
- // zero or more `-capability` options.
-
- CommandLineArg operand;
- SLANG_RETURN_ON_FAIL(reader.expectArg(operand));
-
- List<UnownedStringSlice> slices;
- StringUtil::split(operand.value.getUnownedSlice(), '+', slices);
- Index sliceCount = slices.getCount();
- for(Index i = 0; i < sliceCount; ++i)
+ const Result res = ReproUtil::extractFilesToDirectory(reproName.value, m_sink);
+ if (SLANG_FAILED(res))
{
- UnownedStringSlice atomName = slices[i];
- CapabilityAtom atom = findCapabilityAtom(atomName);
- if( atom == CapabilityAtom::Invalid )
- {
- sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
- return SLANG_FAIL;
- }
-
- addCapabilityAtom(getCurrentTarget(), atom);
+ m_sink->diagnose(reproName.loc, Diagnostics::unableExtractReproToDirectory, reproName.value);
+ return res;
}
- break;
}
- case OptionKind::Stage:
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+ break;
+ }
+ case OptionKind::ModuleName:
+ {
+ CommandLineArg moduleName;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(moduleName));
- Stage stage = findStageByName(name.value);
- if( stage == Stage::Unknown )
- {
- sink->diagnose(name.loc, Diagnostics::unknownStage, name.value);
- return SLANG_FAIL;
- }
- else
- {
- setStage(getCurrentEntryPoint(), stage);
- }
- break;
- }
- case OptionKind::EntryPointName:
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
+ m_compileRequest->setDefaultModuleName(moduleName.value.getBuffer());
+ break;
+ }
+ case OptionKind::LoadRepro: SLANG_RETURN_ON_FAIL(_parseLoadRepro(arg)); break;
+ case OptionKind::LoadReproDirectory:
+ {
+ CommandLineArg reproDirectory;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(reproDirectory));
- RawEntryPoint rawEntryPoint;
- rawEntryPoint.name = name.value;
- rawEntryPoint.translationUnitIndex = currentTranslationUnitIndex;
+ SLANG_RETURN_ON_FAIL(_compileReproDirectory(m_session, m_requestImpl, reproDirectory.value));
+ break;
+ }
+ case OptionKind::ReproFileSystem: SLANG_RETURN_ON_FAIL(_parseReproFileSystem(arg)); break;
+ case OptionKind::SerialIr: m_frontEndReq->useSerialIRBottleneck = true; break;
+ case OptionKind::DisableSpecialization: m_requestImpl->disableSpecialization = true; break;
+ case OptionKind::DisableDynamicDispatch: m_requestImpl->disableDynamicDispatch = true; break;
+ case OptionKind::TrackLiveness: m_requestImpl->setTrackLiveness(true); break;
+ case OptionKind::VerbosePaths: m_requestImpl->getSink()->setFlag(DiagnosticSink::Flag::VerbosePath); break;
+ case OptionKind::DumpWarningDiagnostics: _dumpDiagnostics(Severity::Warning); break;
+ case OptionKind::WarningsAsErrors:
+ {
+ CommandLineArg operand;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(operand));
- rawEntryPoints.add(rawEntryPoint);
- break;
- }
- case OptionKind::Language:
+ if (operand.value == "all")
{
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- const SourceLanguage sourceLanguage = (SourceLanguage)TypeTextUtil::findSourceLanguage(name.value.getUnownedSlice());
-
- if (sourceLanguage == SourceLanguage::Unknown)
- {
- sink->diagnose(name.loc, Diagnostics::unknownSourceLanguage, name.value);
- return SLANG_FAIL;
- }
- else
- {
- while (reader.hasArg() && !reader.peekValue().startsWith("-"))
- {
- SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer(), sourceLanguage));
- }
- }
- break;
+ // TODO(JS):
+ // Perhaps there needs to be a way to disable this selectively.
+ m_requestImpl->getSink()->setFlag(DiagnosticSink::Flag::TreatWarningsAsErrors);
}
- case OptionKind::PassThrough:
+ else
{
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
- if (SLANG_FAILED(TypeTextUtil::findPassThrough(name.value.getUnownedSlice(), passThrough)))
- {
- sink->diagnose(name.loc, Diagnostics::unknownPassThroughTarget, name.value);
- return SLANG_FAIL;
- }
-
- compileRequest->setPassThrough(passThrough);
- break;
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Error));
}
- case OptionKind::MacroDefine:
- {
- // The value to be defined might be part of the same option, as in:
- // -DFOO
- // or it might come separately, as in:
- // -D FOO
+ break;
+ }
+ case OptionKind::DisableWarnings:
+ {
+ CommandLineArg operand;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(operand));
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Disable));
+ break;
+ }
+ case OptionKind::DisableWarning:
+ {
+ // 5 because -Wno-
+ auto name = argValue.getUnownedSlice().tail(5);
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Disable));
+ break;
+ }
+ case OptionKind::EnableWarning:
+ {
+ // 2 because -W
+ auto name = argValue.getUnownedSlice().tail(2);
+ // Enable the warning
+ SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning));
+ break;
+ }
+ case OptionKind::VerifyDebugSerialIr: m_frontEndReq->verifyDebugSerialization = true; break;
+ case OptionKind::ValidateIr: m_frontEndReq->shouldValidateIR = true; break;
+ case OptionKind::SkipCodeGen: m_requestImpl->m_shouldSkipCodegen = true; break;
+ case OptionKind::ParameterBlocksUseRegisterSpaces:
+ {
+ getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES;
+ break;
+ }
+ case OptionKind::IrCompression:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
- UnownedStringSlice slice = argValue.getUnownedSlice().tail(2);
+ SLANG_RETURN_ON_FAIL(SerialParseUtil::parseCompressionType(name.value.getUnownedSlice(), m_requestImpl->getLinkage()->serialCompressionType));
+ break;
+ }
+ case OptionKind::Target:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
- CommandLineArg nextArg;
- if (slice.getLength() <= 0)
- {
- SLANG_RETURN_ON_FAIL(reader.expectArg(nextArg));
- slice = nextArg.value.getUnownedSlice();
- }
+ const CodeGenTarget format = (CodeGenTarget)TypeTextUtil::findCompileTargetFromName(name.value.getUnownedSlice());
- // The string that sets up the define can have an `=` between
- // the name to be defined and its value, so we search for one.
- const Index equalIndex = slice.indexOf('=');
+ if (format == CodeGenTarget::Unknown)
+ {
+ m_sink->diagnose(name.loc, Diagnostics::unknownCodeGenerationTarget, name.value);
+ return SLANG_FAIL;
+ }
- // Now set the preprocessor define
+ RawTarget rawTarget;
+ rawTarget.format = CodeGenTarget(format);
- if (equalIndex >= 0)
- {
- // If we found an `=`, we split the string...
- compileRequest->addPreprocessorDefine(String(slice.head(equalIndex)).getBuffer(), String(slice.tail(equalIndex + 1)).getBuffer());
- }
- else
- {
- // If there was no `=`, then just #define it to an empty string
- compileRequest->addPreprocessorDefine(String(slice).getBuffer(), "");
- }
- break;
- }
- case OptionKind::Include:
- {
- // The value to be defined might be part of the same option, as in:
- // -IFOO
- // or it might come separately, as in:
- // -I FOO
- // (see handling of `-D` above)
- UnownedStringSlice slice = argValue.getUnownedSlice().tail(2);
-
- CommandLineArg nextArg;
- if (slice.getLength() <= 0)
- {
- // Need to read another argument from the command line
- SLANG_RETURN_ON_FAIL(reader.expectArg(nextArg));
- slice = nextArg.value.getUnownedSlice();
- }
+ m_rawTargets.add(rawTarget);
+ break;
+ }
+ case OptionKind::VulkanBindShift:
+ {
+ // -fvk-{b|s|t|u}-shift
+ const auto slice = arg.value.getUnownedSlice().subString(5, 1);
+ HLSLToVulkanLayoutOptions::Kind kind;
+ SLANG_RETURN_ON_FAIL(_getValue(arg, slice, kind));
- compileRequest->addSearchPath(String(slice).getBuffer());
- break;
- }
- case OptionKind::Output:
+ Int shift;
+ SLANG_RETURN_ON_FAIL(_expectInt(arg, shift));
+
+ if (m_reader.hasArg() && m_reader.peekArg().value == toSlice("all"))
{
- //
- // A `-o` option is used to specify a desired output file.
- CommandLineArg outputPath;
- SLANG_RETURN_ON_FAIL(reader.expectArg(outputPath));
-
- addOutputPath(outputPath.value.getBuffer());
- break;
+ m_reader.advance();
+ m_hlslToVulkanLayoutOptions->setAllShift(kind, shift);
}
- case OptionKind::DepFile:
+ else
{
- CommandLineArg dependencyPath;
- SLANG_RETURN_ON_FAIL(reader.expectArg(dependencyPath));
+ Int set;
+ SLANG_RETURN_ON_FAIL(_expectInt(arg, set));
+ m_hlslToVulkanLayoutOptions->setShift(kind, set, shift);
+ }
+ break;
+ }
+ case OptionKind::VulkanBindGlobals:
+ {
+ // -fvk-bind-globals N M
+ Int binding, bindingSet;
+ SLANG_RETURN_ON_FAIL(_expectInt(arg, binding));
+ SLANG_RETURN_ON_FAIL(_expectInt(arg, bindingSet));
- if (requestImpl->m_dependencyOutputPath.getLength() == 0)
- {
- requestImpl->m_dependencyOutputPath = dependencyPath.value;
- }
- else
+ m_hlslToVulkanLayoutOptions->m_globalsBindingSet = Index(bindingSet);
+ m_hlslToVulkanLayoutOptions->m_globalsBinding = Index(binding);
+ break;
+ }
+ case OptionKind::Profile: SLANG_RETURN_ON_FAIL(_parseProfile(arg)); break;
+ case OptionKind::Capability:
+ {
+ // The `-capability` option is similar to `-profile` but does not set the actual profile
+ // for a target (it just adds capabilities).
+ //
+ // TODO: Once profiles are treated as capabilities themselves, it might be possible
+ // to treat `-profile` and `-capability` as aliases, although there might still be
+ // value in only allowing a single `-profile` option per target while still allowing
+ // zero or more `-capability` options.
+
+ CommandLineArg operand;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(operand));
+
+ List<UnownedStringSlice> slices;
+ StringUtil::split(operand.value.getUnownedSlice(), '+', slices);
+ Index sliceCount = slices.getCount();
+ for (Index i = 0; i < sliceCount; ++i)
+ {
+ UnownedStringSlice atomName = slices[i];
+ CapabilityAtom atom = findCapabilityAtom(atomName);
+ if (atom == CapabilityAtom::Invalid)
{
- sink->diagnose(dependencyPath.loc, Diagnostics::duplicateDependencyOutputPaths);
+ m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName);
return SLANG_FAIL;
}
- break;
- }
- case OptionKind::MatrixLayoutRow: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor); break;
- case OptionKind::MatrixLayoutColumn: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor); break;
- case OptionKind::LineDirectiveMode:
- {
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::LineDirectiveMode, reader, sink, value));
- compileRequest->setLineDirectiveMode(SlangLineDirectiveMode(value));
- break;
- }
- case OptionKind::FloatingPointMode:
- {
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FloatingPointMode, reader, sink, value));
- setFloatingPointMode(getCurrentTarget(), FloatingPointMode(value));
- break;
+
+ addCapabilityAtom(getCurrentTarget(), atom);
}
- case OptionKind::GLSLForceScalarLayout:
+ break;
+ }
+ case OptionKind::Stage:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
+
+ Stage stage = findStageByName(name.value);
+ if (stage == Stage::Unknown)
{
- getCurrentTarget()->forceGLSLScalarLayout = true;
- break;
+ m_sink->diagnose(name.loc, Diagnostics::unknownStage, name.value);
+ return SLANG_FAIL;
}
- case OptionKind::EnableEffectAnnotations:
+ else
{
- compileRequest->setEnableEffectAnnotations(true);
- break;
+ setStage(getCurrentEntryPoint(), stage);
}
- case OptionKind::Optimization:
- {
- UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2);
- SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT;
+ break;
+ }
+ case OptionKind::GLSLForceScalarLayout:
+ {
+ getCurrentTarget()->forceGLSLScalarLayout = true;
+ break;
+ }
+ case OptionKind::EnableEffectAnnotations:
+ {
+ m_compileRequest->setEnableEffectAnnotations(true);
+ break;
+ }
+
+ case OptionKind::EntryPointName:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
- if (levelSlice.getLength())
- {
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_getValue(ValueCategory::OptimizationLevel, arg, levelSlice, sink, value));
- level = SlangOptimizationLevel(value);
- }
+ RawEntryPoint rawEntryPoint;
+ rawEntryPoint.name = name.value;
+ rawEntryPoint.translationUnitIndex = m_currentTranslationUnitIndex;
- compileRequest->setOptimizationLevel(level);
- break;
- }
- case OptionKind::DebugInformation:
- {
- auto name = argValue.getUnownedSlice().tail(2);
+ m_rawEntryPoints.add(rawEntryPoint);
+ break;
+ }
+ case OptionKind::Language:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
- // Note: unlike with `-O` above, we have to consider that other
- // options might have names that start with `-g` and so cannot
- // just detect it as a prefix.
- if (name.getLength() == 0)
- {
- // The default is standard
- compileRequest->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_STANDARD);
- }
- else
- {
- CommandOptions::UserValue value;
- ValueCategory valueCat;
- ValueCategory valueCats[] = { ValueCategory::DebugLevel, ValueCategory::DebugInfoFormat };
- SLANG_RETURN_ON_FAIL(_getValue(makeConstArrayView(valueCats), arg, name, sink, valueCat, value));
-
- if (valueCat == ValueCategory::DebugLevel)
- {
- const auto level = (SlangDebugInfoLevel)value;
- compileRequest->setDebugInfoLevel(level);
- }
- else
- {
- const auto debugFormat = (SlangDebugInfoFormat)value;
- compileRequest->setDebugInfoFormat(debugFormat);
- }
- }
- break;
+ const SourceLanguage sourceLanguage = (SourceLanguage)TypeTextUtil::findSourceLanguage(name.value.getUnownedSlice());
+
+ if (sourceLanguage == SourceLanguage::Unknown)
+ {
+ m_sink->diagnose(name.loc, Diagnostics::unknownSourceLanguage, name.value);
+ return SLANG_FAIL;
}
- case OptionKind::DefaultImageFormatUnknown: requestImpl->useUnknownImageFormatAsDefault = true; break;
- case OptionKind::Obfuscate: requestImpl->getLinkage()->m_obfuscateCode = true; break;
- case OptionKind::FileSystem:
+ else
{
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FileSystemType, reader, sink, value));
- typedef TypeTextUtil::FileSystemType FileSystemType;
-
- switch (FileSystemType(value))
+ while (m_reader.hasArg() && !m_reader.peekValue().startsWith("-"))
{
- case FileSystemType::Default: compileRequest->setFileSystem(nullptr); break;
- case FileSystemType::LoadFile: compileRequest->setFileSystem(OSFileSystem::getLoadSingleton()); break;
- case FileSystemType::Os: compileRequest->setFileSystem(OSFileSystem::getExtSingleton()); break;
+ SLANG_RETURN_ON_FAIL(addInputPath(m_reader.getValueAndAdvance().getBuffer(), sourceLanguage));
}
- break;
}
- case OptionKind::ReferenceModule:
+ break;
+ }
+ case OptionKind::PassThrough:
+ {
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
+
+ SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
+ if (SLANG_FAILED(TypeTextUtil::findPassThrough(name.value.getUnownedSlice(), passThrough)))
{
- CommandLineArg referenceModuleName;
- SLANG_RETURN_ON_FAIL(reader.expectArg(referenceModuleName));
+ m_sink->diagnose(name.loc, Diagnostics::unknownPassThroughTarget, name.value);
+ return SLANG_FAIL;
+ }
- const auto path = referenceModuleName.value;
+ m_compileRequest->setPassThrough(passThrough);
+ break;
+ }
+ case OptionKind::MacroDefine:
+ {
+ // The value to be defined might be part of the same option, as in:
+ // -DFOO
+ // or it might come separately, as in:
+ // -D FOO
- auto desc = ArtifactDescUtil::getDescFromPath(path.getUnownedSlice());
+ UnownedStringSlice slice = argValue.getUnownedSlice().tail(2);
- if (desc.kind == ArtifactKind::Unknown)
- {
- sink->diagnose(referenceModuleName.loc, Diagnostics::unknownLibraryKind, Path::getPathExt(path));
- return SLANG_FAIL;
- }
+ CommandLineArg nextArg;
+ if (slice.getLength() <= 0)
+ {
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(nextArg));
+ slice = nextArg.value.getUnownedSlice();
+ }
- // If it's a GPU binary, then we'll assume it's a library
- if (ArtifactDescUtil::isGpuUsable(desc))
- {
- desc.kind = ArtifactKind::Library;
- }
+ // The string that sets up the define can have an `=` between
+ // the name to be defined and its value, so we search for one.
+ const Index equalIndex = slice.indexOf('=');
- // If its a zip we'll *assume* its a zip holding compilation results
- if (desc.kind == ArtifactKind::Zip)
- {
- desc.payload = ArtifactPayload::CompileResults;
- }
-
- if (!ArtifactDescUtil::isLinkable(desc))
- {
- sink->diagnose(referenceModuleName.loc, Diagnostics::kindNotLinkable, Path::getPathExt(path));
- return SLANG_FAIL;
- }
+ // Now set the preprocessor define
- const String name = ArtifactDescUtil::getBaseNameFromPath(desc, path.getUnownedSlice());
+ if (equalIndex >= 0)
+ {
+ // If we found an `=`, we split the string...
+ m_compileRequest->addPreprocessorDefine(String(slice.head(equalIndex)).getBuffer(), String(slice.tail(equalIndex + 1)).getBuffer());
+ }
+ else
+ {
+ // If there was no `=`, then just #define it to an empty string
+ m_compileRequest->addPreprocessorDefine(String(slice).getBuffer(), "");
+ }
+ break;
+ }
+ case OptionKind::Include:
+ {
+ // The value to be defined might be part of the same option, as in:
+ // -IFOO
+ // or it might come separately, as in:
+ // -I FOO
+ // (see handling of `-D` above)
+ UnownedStringSlice slice = argValue.getUnownedSlice().tail(2);
- // Create the artifact
- auto artifact = Artifact::create(desc, name.getUnownedSlice());
+ CommandLineArg nextArg;
+ if (slice.getLength() <= 0)
+ {
+ // Need to read another argument from the command line
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(nextArg));
+ slice = nextArg.value.getUnownedSlice();
+ }
- // There is a problem here if I want to reference a library that is a 'system' library or is not directly a file
- // In that case the path shouldn't be set and the name should completely define the library.
- // Seeing as on all targets the baseName doesn't have an extension, and all library types do
- // if the name doesn't have an extension we can assume there is no path to it.
-
- ComPtr<IOSFileArtifactRepresentation> fileRep;
- if (Path::getPathExt(path).getLength() <= 0)
- {
- // If there is no extension *assume* it is the name of a system level library
- fileRep = new OSFileArtifactRepresentation(IOSFileArtifactRepresentation::Kind::NameOnly, path.getUnownedSlice(), nullptr);
- }
- else
- {
- fileRep = new OSFileArtifactRepresentation(IOSFileArtifactRepresentation::Kind::Reference, path.getUnownedSlice(), nullptr);
- if (!fileRep->exists())
- {
- sink->diagnose(referenceModuleName.loc, Diagnostics::libraryDoesNotExist, path);
- return SLANG_FAIL;
- }
- }
- artifact->addRepresentation(fileRep);
+ m_compileRequest->addSearchPath(String(slice).getBuffer());
+ break;
+ }
+ case OptionKind::Output:
+ {
+ //
+ // A `-o` option is used to specify a desired output file.
+ CommandLineArg outputPath;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(outputPath));
- SLANG_RETURN_ON_FAIL(_addLibraryReference(requestImpl, artifact));
- break;
+ addOutputPath(outputPath.value.getBuffer());
+ break;
+ }
+ case OptionKind::DepFile:
+ {
+ CommandLineArg dependencyPath;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(dependencyPath));
+
+ if (m_requestImpl->m_dependencyOutputPath.getLength() == 0)
+ {
+ m_requestImpl->m_dependencyOutputPath = dependencyPath.value;
}
- case OptionKind::Version:
+ else
{
- sink->diagnoseRaw(Severity::Note, session->getBuildTagString());
- break;
+ m_sink->diagnose(dependencyPath.loc, Diagnostics::duplicateDependencyOutputPaths);
+ return SLANG_FAIL;
}
- case OptionKind::HelpStyle:
+ break;
+ }
+ case OptionKind::MatrixLayoutRow: m_defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor); break;
+ case OptionKind::MatrixLayoutColumn: m_defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor); break;
+ case OptionKind::LineDirectiveMode:
+ {
+ SlangLineDirectiveMode value;
+ SLANG_RETURN_ON_FAIL(_expectValue(value));
+ m_compileRequest->setLineDirectiveMode(value);
+ break;
+ }
+ case OptionKind::FloatingPointMode:
+ {
+ FloatingPointMode value;
+ SLANG_RETURN_ON_FAIL(_expectValue(value));
+ setFloatingPointMode(getCurrentTarget(), value);
+ break;
+ }
+ case OptionKind::Optimization:
+ {
+ UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2);
+ SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT;
+
+ if (levelSlice.getLength())
{
- CommandOptions::UserValue value;
- SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::HelpStyle, reader, sink, value));
- helpStyle = CommandOptionsWriter::Style(value);
- break;
+ SLANG_RETURN_ON_FAIL(_getValue(arg, levelSlice, level));
}
- case OptionKind::Help:
+
+ m_compileRequest->setOptimizationLevel(level);
+ break;
+ }
+ case OptionKind::DebugInformation: SLANG_RETURN_ON_FAIL(_parseDebugInformation(arg)); break;
+ case OptionKind::DefaultImageFormatUnknown: m_requestImpl->useUnknownImageFormatAsDefault = true; break;
+ case OptionKind::Obfuscate: m_requestImpl->getLinkage()->m_obfuscateCode = true; break;
+ case OptionKind::FileSystem:
+ {
+ typedef TypeTextUtil::FileSystemType FileSystemType;
+ FileSystemType value;
+ SLANG_RETURN_ON_FAIL(_expectValue(value));
+
+ switch (value)
{
- Index categoryIndex = -1;
+ case FileSystemType::Default: m_compileRequest->setFileSystem(nullptr); break;
+ case FileSystemType::LoadFile: m_compileRequest->setFileSystem(OSFileSystem::getLoadSingleton()); break;
+ case FileSystemType::Os: m_compileRequest->setFileSystem(OSFileSystem::getExtSingleton()); break;
+ }
+ break;
+ }
+ case OptionKind::ReferenceModule: SLANG_RETURN_ON_FAIL(_parseReferenceModule(arg)); break;
+ case OptionKind::Version:
+ {
+ m_sink->diagnoseRaw(Severity::Note, m_session->getBuildTagString());
+ break;
+ }
+ case OptionKind::HelpStyle: SLANG_RETURN_ON_FAIL(_expectValue(m_helpStyle)); break;
+ case OptionKind::Help:
+ {
+ SLANG_RETURN_ON_FAIL(_parseHelp(arg));
- if (reader.hasArg())
- {
- auto catArg = reader.getArgAndAdvance();
-
- categoryIndex = options.findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice());
- if (categoryIndex < 0)
- {
- sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory);
- return SLANG_FAIL;
- }
- }
+ // We retun an error so after this has successfully passed, we quit
+ return SLANG_FAIL;
+ }
+ case OptionKind::EmitSpirvDirectly: getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; break;
- CommandOptionsWriter::Options writerOptions;
- writerOptions.style = helpStyle;
+ case OptionKind::DefaultDownstreamCompiler:
+ {
+ CommandLineArg sourceLanguageArg, compilerArg;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(sourceLanguageArg));
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(compilerArg));
- auto writer = CommandOptionsWriter::create(writerOptions);
+ SlangSourceLanguage sourceLanguage = TypeTextUtil::findSourceLanguage(sourceLanguageArg.value.getUnownedSlice());
+ if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN)
+ {
+ m_sink->diagnose(sourceLanguageArg.loc, Diagnostics::unknownSourceLanguage, sourceLanguageArg.value);
+ return SLANG_FAIL;
+ }
- auto& buf = writer->getBuilder();
+ SlangPassThrough compiler;
+ if (SLANG_FAILED(TypeTextUtil::findPassThrough(compilerArg.value.getUnownedSlice(), compiler)))
+ {
+ m_sink->diagnose(compilerArg.loc, Diagnostics::unknownPassThroughTarget, compilerArg.value);
+ return SLANG_FAIL;
+ }
- if (categoryIndex < 0)
- {
- // If it's the text style we can inject usage at the top
- if (helpStyle == CommandOptionsWriter::Style::Text)
- {
- _appendUsageTitle(buf);
- }
- else
- {
- // NOTE! We need this preamble because if we have links,
- // we have to make sure the first thing in markdown *isn't* <>
-
- buf << "# Slang Command Line Options\n\n";
- buf << "*Usage:*\n";
- buf << "```\n";
- buf << "slangc [options...] [--] <input files>\n\n";
- buf << "# For help\n";
- buf << "slangc -h\n\n";
- buf << "# To generate this file\n";
- buf << "slangc -help-style markdown -h\n";
- buf << "```\n";
- }
-
- writer->appendDescription(&options);
- }
- else
- {
- writer->appendDescriptionForCategory(&options, categoryIndex);
- }
-
- sink->diagnoseRaw(Severity::Note, buf.getBuffer());
-
+ if (SLANG_FAILED(m_session->setDefaultDownstreamCompiler(sourceLanguage, compiler)))
+ {
+ m_sink->diagnose(arg.loc, Diagnostics::unableToSetDefaultDownstreamCompiler, compilerArg.value, sourceLanguageArg.value);
return SLANG_FAIL;
}
- case OptionKind::EmitSpirvDirectly: getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; break;
-
- case OptionKind::DefaultDownstreamCompiler:
+ break;
+ }
+ case OptionKind::CompilerPath:
+ {
+ const Index index = argValue.lastIndexOf('-');
+ if (index >= 0)
{
- CommandLineArg sourceLanguageArg, compilerArg;
- SLANG_RETURN_ON_FAIL(reader.expectArg(sourceLanguageArg));
- SLANG_RETURN_ON_FAIL(reader.expectArg(compilerArg));
+ CommandLineArg name;
+ SLANG_RETURN_ON_FAIL(m_reader.expectArg(name));
- SlangSourceLanguage sourceLanguage = TypeTextUtil::findSourceLanguage(sourceLanguageArg.value.getUnownedSlice());
- if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN)
- {
- sink->diagnose(sourceLanguageArg.loc, Diagnostics::unknownSourceLanguage, sourceLanguageArg.value);
- return SLANG_FAIL;
- }
+ UnownedStringSlice passThroughSlice = argValue.getUnownedSlice().head(index).tail(1);
- SlangPassThrough compiler;
- if (SLANG_FAILED(TypeTextUtil::findPassThrough(compilerArg.value.getUnownedSlice(), compiler)))
+ // Skip the initial -, up to the last -
+ SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
+ if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(passThroughSlice, passThrough)))
{
- sink->diagnose(compilerArg.loc, Diagnostics::unknownPassThroughTarget, compilerArg.value);
- return SLANG_FAIL;
+ m_session->setDownstreamCompilerPath(passThrough, name.value.getBuffer());
+ continue;
}
-
- if (SLANG_FAILED(session->setDefaultDownstreamCompiler(sourceLanguage, compiler)))
+ else
{
- sink->diagnose(arg.loc, Diagnostics::unableToSetDefaultDownstreamCompiler, compilerArg.value, sourceLanguageArg.value);
+ m_sink->diagnose(arg.loc, Diagnostics::unknownDownstreamCompiler, passThroughSlice);
return SLANG_FAIL;
}
- break;
- }
- case OptionKind::CompilerPath:
- {
- const Index index = argValue.lastIndexOf('-');
- if (index >= 0)
- {
- CommandLineArg name;
- SLANG_RETURN_ON_FAIL(reader.expectArg(name));
-
- UnownedStringSlice passThroughSlice = argValue.getUnownedSlice().head(index).tail(1);
-
- // Skip the initial -, up to the last -
- SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE;
- if (SLANG_SUCCEEDED(TypeTextUtil::findPassThrough(passThroughSlice, passThrough)))
- {
- session->setDownstreamCompilerPath(passThrough, name.value.getBuffer());
- continue;
- }
- else
- {
- sink->diagnose(arg.loc, Diagnostics::unknownDownstreamCompiler, passThroughSlice);
- return SLANG_FAIL;
- }
- }
- break;
}
- case OptionKind::InputFilesRemain:
- {
- // The `--` option causes us to stop trying to parse options,
- // and treat the rest of the command line as input file names:
- while (reader.hasArg())
- {
- SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer()));
- }
- break;
- }
- default:
+ break;
+ }
+ case OptionKind::InputFilesRemain:
+ {
+ // The `--` option causes us to stop trying to parse options,
+ // and treat the rest of the command line as input file names:
+ while (m_reader.hasArg())
{
- // Hmmm, we looked up and produced a valid enum, but it wasn't handled in the switch...
- sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
-
- _outputMinimalUsage(sink);
- return SLANG_FAIL;
+ SLANG_RETURN_ON_FAIL(addInputPath(m_reader.getValueAndAdvance().getBuffer()));
}
+ break;
}
- }
+ default:
+ {
+ // Hmmm, we looked up and produced a valid enum, but it wasn't handled in the switch...
+ m_sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue);
- if (compileStdLib)
- {
- SLANG_RETURN_ON_FAIL(session->compileStdLib(compileStdLibFlags));
+ _outputMinimalUsage();
+ return SLANG_FAIL;
+ }
}
+ }
+
+ // If there are no layout settings, we don't need to carry this state
+ if (m_hlslToVulkanLayoutOptions->isDefault())
+ {
+ m_hlslToVulkanLayoutOptions.setNull();
+ }
+
+ if (m_compileStdLib)
+ {
+ SLANG_RETURN_ON_FAIL(m_session->compileStdLib(m_compileStdLibFlags));
+ }
+
+ // TODO(JS): This is a restriction because of how setting of state works for load repro
+ // If a repro has been loaded, then many of the following options will overwrite
+ // what was set up. So for now they are ignored, and only parameters set as part
+ // of the loop work if they are after -load-repro
+ if (m_hasLoadedRepro)
+ {
+ return SLANG_OK;
+ }
- // TODO(JS): This is a restriction because of how setting of state works for load repro
- // If a repro has been loaded, then many of the following options will overwrite
- // what was set up. So for now they are ignored, and only parameters set as part
- // of the loop work if they are after -load-repro
- if (hasLoadedRepro)
+ m_compileRequest->setCompileFlags(m_flags);
+
+ // As a compatability feature, if the user didn't list any explicit entry
+ // point names, *and* they are compiling a single translation unit, *and* they
+ // have either specified a stage, or we can assume one from the naming
+ // of the translation unit, then we assume they wanted to compile a single
+ // entry point named `main`.
+ //
+ if (m_rawEntryPoints.getCount() == 0
+ && m_rawTranslationUnits.getCount() == 1
+ && (m_defaultEntryPoint.stage != Stage::Unknown
+ || m_rawTranslationUnits[0].impliedStage != Stage::Unknown))
+ {
+ RawEntryPoint entry;
+ entry.name = "main";
+ entry.translationUnitIndex = 0;
+ m_rawEntryPoints.add(entry);
+ }
+
+ // If the user (manually or implicitly) specified only a single entry point,
+ // then we allow the associated stage to be specified either before or after
+ // the entry point. This means that if there is a stage attached
+ // to the "default" entry point, we should copy it over to the
+ // explicit one.
+ //
+ if (m_rawEntryPoints.getCount() == 1)
+ {
+ if (m_defaultEntryPoint.stage != Stage::Unknown)
{
- return SLANG_OK;
+ setStage(getCurrentEntryPoint(), m_defaultEntryPoint.stage);
}
- compileRequest->setCompileFlags(flags);
-
- // As a compatability feature, if the user didn't list any explicit entry
- // point names, *and* they are compiling a single translation unit, *and* they
- // have either specified a stage, or we can assume one from the naming
- // of the translation unit, then we assume they wanted to compile a single
- // entry point named `main`.
- //
- if(rawEntryPoints.getCount() == 0
- && rawTranslationUnits.getCount() == 1
- && (defaultEntryPoint.stage != Stage::Unknown
- || rawTranslationUnits[0].impliedStage != Stage::Unknown))
- {
- RawEntryPoint entry;
- entry.name = "main";
- entry.translationUnitIndex = 0;
- rawEntryPoints.add(entry);
- }
-
- // If the user (manually or implicitly) specified only a single entry point,
- // then we allow the associated stage to be specified either before or after
- // the entry point. This means that if there is a stage attached
- // to the "default" entry point, we should copy it over to the
- // explicit one.
+ if (m_defaultEntryPoint.redundantStageSet)
+ getCurrentEntryPoint()->redundantStageSet = true;
+ if (m_defaultEntryPoint.conflictingStagesSet)
+ getCurrentEntryPoint()->conflictingStagesSet = true;
+ }
+ else
+ {
+ // If the "default" entry point has had a stage (or
+ // other state, if we add other per-entry-point state)
+ // specified, but there is more than one entry point,
+ // then that state doesn't apply to anything and we
+ // should issue an error to tell the user something
+ // funky is going on.
//
- if( rawEntryPoints.getCount() == 1 )
+ if (m_defaultEntryPoint.stage != Stage::Unknown)
{
- if(defaultEntryPoint.stage != Stage::Unknown)
+ if (m_rawEntryPoints.getCount() == 0)
{
- setStage(getCurrentEntryPoint(), defaultEntryPoint.stage);
+ m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseNoEntryPoints);
}
-
- if(defaultEntryPoint.redundantStageSet)
- getCurrentEntryPoint()->redundantStageSet = true;
- if(defaultEntryPoint.conflictingStagesSet)
- getCurrentEntryPoint()->conflictingStagesSet = true;
- }
- else
- {
- // If the "default" entry point has had a stage (or
- // other state, if we add other per-entry-point state)
- // specified, but there is more than one entry point,
- // then that state doesn't apply to anything and we
- // should issue an error to tell the user something
- // funky is going on.
- //
- if( defaultEntryPoint.stage != Stage::Unknown )
+ else
{
- if( rawEntryPoints.getCount() == 0 )
- {
- sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseNoEntryPoints);
- }
- else
- {
- sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseBeforeAllEntryPoints);
- }
+ m_sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseBeforeAllEntryPoints);
}
}
+ }
- // Slang requires that every explicit entry point indicate the translation
- // unit it comes from. If there is only one translation unit specified,
- // then implicitly all entry points come from it.
- //
- if(translationUnitCount == 1)
+ // Slang requires that every explicit entry point indicate the translation
+ // unit it comes from. If there is only one translation unit specified,
+ // then implicitly all entry points come from it.
+ //
+ if (m_translationUnitCount == 1)
+ {
+ for (auto& entryPoint : m_rawEntryPoints)
{
- for( auto& entryPoint : rawEntryPoints )
- {
- entryPoint.translationUnitIndex = 0;
- }
+ entryPoint.translationUnitIndex = 0;
}
- else
+ }
+ else
+ {
+ // Otherwise, we require that all entry points be specified after
+ // the translation unit to which tye belong.
+ bool anyEntryPointWithoutTranslationUnit = false;
+ for (auto& entryPoint : m_rawEntryPoints)
{
- // Otherwise, we require that all entry points be specified after
- // the translation unit to which tye belong.
- bool anyEntryPointWithoutTranslationUnit = false;
- for( auto& entryPoint : rawEntryPoints )
- {
- // Skip entry points that are already associated with a translation unit...
- if( entryPoint.translationUnitIndex != -1 )
- continue;
+ // Skip entry points that are already associated with a translation unit...
+ if (entryPoint.translationUnitIndex != -1)
+ continue;
- anyEntryPointWithoutTranslationUnit = true;
- }
- if( anyEntryPointWithoutTranslationUnit )
- {
- sink->diagnose(SourceLoc(), Diagnostics::entryPointsNeedToBeAssociatedWithTranslationUnits);
- return SLANG_FAIL;
- }
+ anyEntryPointWithoutTranslationUnit = true;
}
-
- // Now that entry points are associated with translation units,
- // we can make one additional pass where if an entry point has
- // no specified stage, but the nameing of its translation unit
- // implies a stage, we will use that (a manual `-stage` annotation
- // will always win out in such a case).
- //
- for( auto& rawEntryPoint : rawEntryPoints )
+ if (anyEntryPointWithoutTranslationUnit)
{
- // Skip entry points that already have a stage.
- if(rawEntryPoint.stage != Stage::Unknown)
- continue;
+ m_sink->diagnose(SourceLoc(), Diagnostics::entryPointsNeedToBeAssociatedWithTranslationUnits);
+ return SLANG_FAIL;
+ }
+ }
- // Sanity check: don't process entry points with no associated translation unit.
- if( rawEntryPoint.translationUnitIndex == -1 )
- continue;
+ // Now that entry points are associated with translation units,
+ // we can make one additional pass where if an entry point has
+ // no specified stage, but the nameing of its translation unit
+ // implies a stage, we will use that (a manual `-stage` annotation
+ // will always win out in such a case).
+ //
+ for (auto& rawEntryPoint : m_rawEntryPoints)
+ {
+ // Skip entry points that already have a stage.
+ if (rawEntryPoint.stage != Stage::Unknown)
+ continue;
- auto impliedStage = rawTranslationUnits[rawEntryPoint.translationUnitIndex].impliedStage;
- if(impliedStage != Stage::Unknown)
- rawEntryPoint.stage = impliedStage;
- }
+ // Sanity check: don't process entry points with no associated translation unit.
+ if (rawEntryPoint.translationUnitIndex == -1)
+ continue;
- // Note: it is possible that some entry points still won't have associated
- // stages at this point, but we don't want to error out here, because
- // those entry points might get stages later, as part of semantic checking,
- // if the corresponding function has a `[shader("...")]` attribute.
+ auto impliedStage = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].impliedStage;
+ if (impliedStage != Stage::Unknown)
+ rawEntryPoint.stage = impliedStage;
+ }
- // Now that we've tried to establish stages for entry points, we can
- // issue diagnostics for cases where stages were set redundantly or
- // in conflicting ways.
- //
- for( auto& rawEntryPoint : rawEntryPoints )
+ // Note: it is possible that some entry points still won't have associated
+ // stages at this point, but we don't want to error out here, because
+ // those entry points might get stages later, as part of semantic checking,
+ // if the corresponding function has a `[shader("...")]` attribute.
+
+ // Now that we've tried to establish stages for entry points, we can
+ // issue diagnostics for cases where stages were set redundantly or
+ // in conflicting ways.
+ //
+ for (auto& rawEntryPoint : m_rawEntryPoints)
+ {
+ if (rawEntryPoint.conflictingStagesSet)
{
- if( rawEntryPoint.conflictingStagesSet )
- {
- sink->diagnose(SourceLoc(), Diagnostics::conflictingStagesForEntryPoint, rawEntryPoint.name);
- }
- else if( rawEntryPoint.redundantStageSet )
- {
- sink->diagnose(SourceLoc(), Diagnostics::sameStageSpecifiedMoreThanOnce, rawEntryPoint.stage, rawEntryPoint.name);
- }
- else if( rawEntryPoint.translationUnitIndex != -1 )
+ m_sink->diagnose(SourceLoc(), Diagnostics::conflictingStagesForEntryPoint, rawEntryPoint.name);
+ }
+ else if (rawEntryPoint.redundantStageSet)
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::sameStageSpecifiedMoreThanOnce, rawEntryPoint.stage, rawEntryPoint.name);
+ }
+ else if (rawEntryPoint.translationUnitIndex != -1)
+ {
+ // As a quality-of-life feature, if the file name implies a particular
+ // stage, but the user manually specified something different for
+ // their entry point, give a warning in case they made a mistake.
+
+ auto& rawTranslationUnit = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex];
+ if (rawTranslationUnit.impliedStage != Stage::Unknown
+ && rawEntryPoint.stage != Stage::Unknown
+ && rawTranslationUnit.impliedStage != rawEntryPoint.stage)
{
- // As a quality-of-life feature, if the file name implies a particular
- // stage, but the user manually specified something different for
- // their entry point, give a warning in case they made a mistake.
-
- auto& rawTranslationUnit = rawTranslationUnits[rawEntryPoint.translationUnitIndex];
- if( rawTranslationUnit.impliedStage != Stage::Unknown
- && rawEntryPoint.stage != Stage::Unknown
- && rawTranslationUnit.impliedStage != rawEntryPoint.stage )
- {
- sink->diagnose(SourceLoc(), Diagnostics::explicitStageDoesntMatchImpliedStage, rawEntryPoint.name, rawEntryPoint.stage, rawTranslationUnit.impliedStage);
- }
+ m_sink->diagnose(SourceLoc(), Diagnostics::explicitStageDoesntMatchImpliedStage, rawEntryPoint.name, rawEntryPoint.stage, rawTranslationUnit.impliedStage);
}
}
+ }
- // If the user is requesting code generation via pass-through,
- // then any entry points they specify need to have a stage set,
- // because fxc/dxc/glslang don't have a facility for taking
- // a named entry point and pulling its stage from an attribute.
- //
- if(_passThroughRequiresStage(requestImpl->m_passThrough) )
+ // If the user is requesting code generation via pass-through,
+ // then any entry points they specify need to have a stage set,
+ // because fxc/dxc/glslang don't have a facility for taking
+ // a named entry point and pulling its stage from an attribute.
+ //
+ if (_passThroughRequiresStage(m_requestImpl->m_passThrough))
+ {
+ for (auto& rawEntryPoint : m_rawEntryPoints)
{
- for( auto& rawEntryPoint : rawEntryPoints )
+ if (rawEntryPoint.stage == Stage::Unknown)
{
- if( rawEntryPoint.stage == Stage::Unknown )
- {
- sink->diagnose(SourceLoc(), Diagnostics::noStageSpecifiedInPassThroughMode, rawEntryPoint.name);
- }
+ m_sink->diagnose(SourceLoc(), Diagnostics::noStageSpecifiedInPassThroughMode, rawEntryPoint.name);
}
}
+ }
- // We now have inferred enough information to add the
- // entry points to our compile request.
- //
- for( auto& rawEntryPoint : rawEntryPoints )
- {
- if(rawEntryPoint.translationUnitIndex < 0)
- continue;
+ // We now have inferred enough information to add the
+ // entry points to our compile request.
+ //
+ for (auto& rawEntryPoint : m_rawEntryPoints)
+ {
+ if (rawEntryPoint.translationUnitIndex < 0)
+ continue;
- auto translationUnitID = rawTranslationUnits[rawEntryPoint.translationUnitIndex].translationUnitID;
+ auto translationUnitID = m_rawTranslationUnits[rawEntryPoint.translationUnitIndex].translationUnitID;
- int entryPointID = compileRequest->addEntryPoint(
- translationUnitID,
- rawEntryPoint.name.begin(),
- SlangStage(rawEntryPoint.stage));
+ int entryPointID = m_compileRequest->addEntryPoint(
+ translationUnitID,
+ rawEntryPoint.name.begin(),
+ SlangStage(rawEntryPoint.stage));
- rawEntryPoint.entryPointID = entryPointID;
- }
+ rawEntryPoint.entryPointID = entryPointID;
+ }
- // We are going to build a mapping from target formats to the
- // target that handles that format.
- Dictionary<CodeGenTarget, int> mapFormatToTargetIndex;
+ // We are going to build a mapping from target formats to the
+ // target that handles that format.
+ Dictionary<CodeGenTarget, int> mapFormatToTargetIndex;
- // If there was no explicit `-target` specified, then we will look
- // at the `-o` options to see what we can infer.
- //
- if(rawTargets.getCount() == 0)
+ // If there was no explicit `-target` specified, then we will look
+ // at the `-o` options to see what we can infer.
+ //
+ if (m_rawTargets.getCount() == 0)
+ {
+ // If there are no targets and no outputs
+ if (m_rawOutputs.getCount() == 0)
{
- // If there are no targets and no outputs
- if (rawOutputs.getCount() == 0)
+ // And we have a container for output, then enable emitting SlangIR module
+ if (m_requestImpl->m_containerFormat != ContainerFormat::None)
{
- // And we have a container for output, then enable emitting SlangIR module
- if (requestImpl->m_containerFormat != ContainerFormat::None)
- {
- requestImpl->m_emitIr = true;
- }
+ m_requestImpl->m_emitIr = true;
}
- else
+ }
+ else
+ {
+ for (auto& rawOutput : m_rawOutputs)
{
- for(auto& rawOutput : rawOutputs)
- {
- // Some outputs don't imply a target format, and we shouldn't use those for inference.
- auto impliedFormat = rawOutput.impliedFormat;
- if( impliedFormat == CodeGenTarget::Unknown )
- continue;
-
- int targetIndex = 0;
- if( !mapFormatToTargetIndex.tryGetValue(impliedFormat, targetIndex) )
- {
- targetIndex = (int) rawTargets.getCount();
+ // Some outputs don't imply a target format, and we shouldn't use those for inference.
+ auto impliedFormat = rawOutput.impliedFormat;
+ if (impliedFormat == CodeGenTarget::Unknown)
+ continue;
- RawTarget rawTarget;
- rawTarget.format = impliedFormat;
- rawTargets.add(rawTarget);
+ int targetIndex = 0;
+ if (!mapFormatToTargetIndex.tryGetValue(impliedFormat, targetIndex))
+ {
+ targetIndex = (int)m_rawTargets.getCount();
- mapFormatToTargetIndex[impliedFormat] = targetIndex;
- }
+ RawTarget rawTarget;
+ rawTarget.format = impliedFormat;
+ m_rawTargets.add(rawTarget);
- rawOutput.targetIndex = targetIndex;
+ mapFormatToTargetIndex[impliedFormat] = targetIndex;
}
+
+ rawOutput.targetIndex = targetIndex;
}
}
- else
+ }
+ else
+ {
+ // If there were explicit targets, then we will use those, but still
+ // build up our mapping. We should object if the same target format
+ // is specified more than once (just because of the ambiguities
+ // it will create).
+ //
+ int targetCount = (int)m_rawTargets.getCount();
+ for (int targetIndex = 0; targetIndex < targetCount; ++targetIndex)
{
- // If there were explicit targets, then we will use those, but still
- // build up our mapping. We should object if the same target format
- // is specified more than once (just because of the ambiguities
- // it will create).
- //
- int targetCount = (int) rawTargets.getCount();
- for(int targetIndex = 0; targetIndex < targetCount; ++targetIndex)
- {
- auto format = rawTargets[targetIndex].format;
+ auto format = m_rawTargets[targetIndex].format;
- if( mapFormatToTargetIndex.containsKey(format) )
- {
- sink->diagnose(SourceLoc(), Diagnostics::duplicateTargets, format);
- }
- else
- {
- mapFormatToTargetIndex[format] = targetIndex;
- }
+ if (mapFormatToTargetIndex.containsKey(format))
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::duplicateTargets, format);
+ }
+ else
+ {
+ mapFormatToTargetIndex[format] = targetIndex;
}
}
+ }
- // If we weren't able to infer any targets from output paths (perhaps
- // because there were no output paths), but there was a profile specified,
- // then we can try to infer a target from the profile.
+ // If we weren't able to infer any targets from output paths (perhaps
+ // because there were no output paths), but there was a profile specified,
+ // then we can try to infer a target from the profile.
+ //
+ if (m_rawTargets.getCount() == 0
+ && m_defaultTarget.profileVersion != ProfileVersion::Unknown
+ && !m_defaultTarget.conflictingProfilesSet)
+ {
+ // Let's see if the chosen profile allows us to infer
+ // the code gen target format that the user probably meant.
//
- if( rawTargets.getCount() == 0
- && defaultTarget.profileVersion != ProfileVersion::Unknown
- && !defaultTarget.conflictingProfilesSet)
+ CodeGenTarget inferredFormat = CodeGenTarget::Unknown;
+ auto profileVersion = m_defaultTarget.profileVersion;
+ switch (Profile(profileVersion).getFamily())
{
- // Let's see if the chosen profile allows us to infer
- // the code gen target format that the user probably meant.
- //
- CodeGenTarget inferredFormat = CodeGenTarget::Unknown;
- auto profileVersion = defaultTarget.profileVersion;
- switch( Profile(profileVersion).getFamily() )
- {
default:
break;
@@ -2415,7 +2595,7 @@ struct OptionsParser
// cases should manually specify `-target dxil`.
//
case ProfileFamily::DX:
- if( profileVersion >= ProfileVersion::DX_6_0 )
+ if (profileVersion >= ProfileVersion::DX_6_0)
{
inferredFormat = CodeGenTarget::DXIL;
}
@@ -2424,225 +2604,227 @@ struct OptionsParser
inferredFormat = CodeGenTarget::DXBytecode;
}
break;
- }
+ }
- if( inferredFormat != CodeGenTarget::Unknown )
- {
- RawTarget rawTarget;
- rawTarget.format = inferredFormat;
- rawTargets.add(rawTarget);
- }
+ if (inferredFormat != CodeGenTarget::Unknown)
+ {
+ RawTarget rawTarget;
+ rawTarget.format = inferredFormat;
+ m_rawTargets.add(rawTarget);
}
+ }
- // Similar to the case for entry points, if there is a single target,
- // then we allow some of its options to come from the "default"
- // target state.
- if(rawTargets.getCount() == 1)
+ // Similar to the case for entry points, if there is a single target,
+ // then we allow some of its options to come from the "default"
+ // target state.
+ if (m_rawTargets.getCount() == 1)
+ {
+ if (m_defaultTarget.profileVersion != ProfileVersion::Unknown)
{
- if(defaultTarget.profileVersion != ProfileVersion::Unknown)
- {
- setProfileVersion(getCurrentTarget(), defaultTarget.profileVersion);
- }
- for( auto atom : defaultTarget.capabilityAtoms )
- {
- addCapabilityAtom(getCurrentTarget(), atom);
- }
+ setProfileVersion(getCurrentTarget(), m_defaultTarget.profileVersion);
+ }
+ for (auto atom : m_defaultTarget.capabilityAtoms)
+ {
+ addCapabilityAtom(getCurrentTarget(), atom);
+ }
- getCurrentTarget()->targetFlags |= defaultTarget.targetFlags;
+ getCurrentTarget()->targetFlags |= m_defaultTarget.targetFlags;
- if( defaultTarget.floatingPointMode != FloatingPointMode::Default )
- {
- setFloatingPointMode(getCurrentTarget(), defaultTarget.floatingPointMode);
- }
+ if (m_defaultTarget.floatingPointMode != FloatingPointMode::Default)
+ {
+ setFloatingPointMode(getCurrentTarget(), m_defaultTarget.floatingPointMode);
}
- else
+ }
+ else
+ {
+ // If the "default" target has had a profile (or other state)
+ // specified, but there is != 1 taget, then that state doesn't
+ // apply to anythign and we should give the user an error.
+ //
+ if (m_defaultTarget.profileVersion != ProfileVersion::Unknown)
{
- // If the "default" target has had a profile (or other state)
- // specified, but there is != 1 taget, then that state doesn't
- // apply to anythign and we should give the user an error.
- //
- if( defaultTarget.profileVersion != ProfileVersion::Unknown )
- {
- if( rawTargets.getCount() == 0 )
- {
- // This should only happen if there were multiple `-profile` options,
- // so we didn't try to infer a target, or if the `-profile` option
- // somehow didn't imply a target.
- //
- sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseNoTargets);
- }
- else
- {
- sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseBeforeAllTargets);
- }
- }
-
- if( defaultTarget.targetFlags )
+ if (m_rawTargets.getCount() == 0)
{
- if( rawTargets.getCount() == 0 )
- {
- sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets);
- }
- else
- {
- sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets);
- }
+ // This should only happen if there were multiple `-profile` options,
+ // so we didn't try to infer a target, or if the `-profile` option
+ // somehow didn't imply a target.
+ //
+ m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseNoTargets);
}
-
- if( defaultTarget.floatingPointMode != FloatingPointMode::Default )
+ else
{
- if( rawTargets.getCount() == 0 )
- {
- sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets);
- }
- else
- {
- sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets);
- }
+ m_sink->diagnose(SourceLoc(), Diagnostics::profileSpecificationIgnoredBecauseBeforeAllTargets);
}
-
}
- for(auto& rawTarget : rawTargets)
+ if (m_defaultTarget.targetFlags)
{
- if( rawTarget.conflictingProfilesSet )
+ if (m_rawTargets.getCount() == 0)
{
- sink->diagnose(SourceLoc(), Diagnostics::conflictingProfilesSpecifiedForTarget, rawTarget.format);
+ m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets);
}
- else if( rawTarget.redundantProfileSet )
+ else
{
- sink->diagnose(SourceLoc(), Diagnostics::sameProfileSpecifiedMoreThanOnce, rawTarget.profileVersion, rawTarget.format);
+ m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets);
}
}
- // TODO: do we need to require that a target must have a profile specified,
- // or will we continue to allow the profile to be inferred from the target?
-
- // We now have enough information to go ahead and declare the targets
- // through the Slang API:
- //
- for(auto& rawTarget : rawTargets)
+ if (m_defaultTarget.floatingPointMode != FloatingPointMode::Default)
{
- int targetID = compileRequest->addCodeGenTarget(SlangCompileTarget(rawTarget.format));
- rawTarget.targetID = targetID;
-
- if( rawTarget.profileVersion != ProfileVersion::Unknown )
+ if (m_rawTargets.getCount() == 0)
{
- compileRequest->setTargetProfile(targetID, SlangProfileID(Profile(rawTarget.profileVersion).raw));
+ m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets);
}
- for( auto atom : rawTarget.capabilityAtoms )
+ else
{
- requestImpl->addTargetCapability(targetID, SlangCapabilityID(atom));
+ m_sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets);
}
+ }
- if( rawTarget.targetFlags )
- {
- compileRequest->setTargetFlags(targetID, rawTarget.targetFlags);
- }
+ }
+ for (auto& rawTarget : m_rawTargets)
+ {
+ if (rawTarget.conflictingProfilesSet)
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::conflictingProfilesSpecifiedForTarget, rawTarget.format);
+ }
+ else if (rawTarget.redundantProfileSet)
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::sameProfileSpecifiedMoreThanOnce, rawTarget.profileVersion, rawTarget.format);
+ }
+ }
- if( rawTarget.floatingPointMode != FloatingPointMode::Default )
- {
- compileRequest->setTargetFloatingPointMode(targetID, SlangFloatingPointMode(rawTarget.floatingPointMode));
- }
- if (rawTarget.forceGLSLScalarLayout)
- {
- compileRequest->setTargetForceGLSLScalarBufferLayout(targetID, true);
- }
+ // TODO: do we need to require that a target must have a profile specified,
+ // or will we continue to allow the profile to be inferred from the target?
+
+ // We now have enough information to go ahead and declare the targets
+ // through the Slang API:
+ //
+ for (auto& rawTarget : m_rawTargets)
+ {
+ int targetID = m_compileRequest->addCodeGenTarget(SlangCompileTarget(rawTarget.format));
+ rawTarget.targetID = targetID;
+
+ m_requestImpl->setHLSLToVulkanLayoutOptions(targetID, m_hlslToVulkanLayoutOptions);
+
+ if (rawTarget.profileVersion != ProfileVersion::Unknown)
+ {
+ m_compileRequest->setTargetProfile(targetID, SlangProfileID(Profile(rawTarget.profileVersion).raw));
+ }
+ for (auto atom : rawTarget.capabilityAtoms)
+ {
+ m_requestImpl->addTargetCapability(targetID, SlangCapabilityID(atom));
}
- if(defaultMatrixLayoutMode != SLANG_MATRIX_LAYOUT_MODE_UNKNOWN)
+ if (rawTarget.targetFlags)
{
- compileRequest->setMatrixLayoutMode(defaultMatrixLayoutMode);
+ m_compileRequest->setTargetFlags(targetID, rawTarget.targetFlags);
}
- // Next we need to sort out the output files specified with `-o`, and
- // figure out which entry point and/or target they apply to.
- //
- // If there is only a single entry point, then that is automatically
- // the entry point that should be associated with all outputs.
- //
- if( rawEntryPoints.getCount() == 1 )
+ if (rawTarget.floatingPointMode != FloatingPointMode::Default)
{
- for( auto& rawOutput : rawOutputs )
- {
- rawOutput.entryPointIndex = 0;
- }
+ m_compileRequest->setTargetFloatingPointMode(targetID, SlangFloatingPointMode(rawTarget.floatingPointMode));
}
- //
- // Similarly, if there is only one target, then all outputs must
- // implicitly appertain to that target.
- //
- if( rawTargets.getCount() == 1 )
+
+ if (rawTarget.forceGLSLScalarLayout)
{
- for( auto& rawOutput : rawOutputs )
- {
- rawOutput.targetIndex = 0;
- }
+ m_compileRequest->setTargetForceGLSLScalarBufferLayout(targetID, true);
}
+ }
- // If we don't have any raw outputs but do have a raw target,
- // add an empty' rawOutput for certain targets where the expected behavior is obvious.
- if (rawOutputs.getCount() == 0 &&
- rawTargets.getCount() == 1 &&
- (rawTargets[0].format == CodeGenTarget::HostCPPSource ||
- rawTargets[0].format == CodeGenTarget::PyTorchCppBinding ||
- rawTargets[0].format == CodeGenTarget::CUDASource ||
- ArtifactDescUtil::makeDescForCompileTarget(asExternal(rawTargets[0].format)).kind == ArtifactKind::HostCallable))
+ if (m_defaultMatrixLayoutMode != SLANG_MATRIX_LAYOUT_MODE_UNKNOWN)
+ {
+ m_compileRequest->setMatrixLayoutMode(m_defaultMatrixLayoutMode);
+ }
+
+ // Next we need to sort out the output files specified with `-o`, and
+ // figure out which entry point and/or target they apply to.
+ //
+ // If there is only a single entry point, then that is automatically
+ // the entry point that should be associated with all outputs.
+ //
+ if (m_rawEntryPoints.getCount() == 1)
+ {
+ for (auto& rawOutput : m_rawOutputs)
+ {
+ rawOutput.entryPointIndex = 0;
+ }
+ }
+ //
+ // Similarly, if there is only one target, then all outputs must
+ // implicitly appertain to that target.
+ //
+ if (m_rawTargets.getCount() == 1)
+ {
+ for (auto& rawOutput : m_rawOutputs)
{
- RawOutput rawOutput;
- rawOutput.impliedFormat = rawTargets[0].format;
rawOutput.targetIndex = 0;
- rawOutputs.add(rawOutput);
}
+ }
- // Consider the output files specified via `-o` and try to figure
- // out how to deal with them.
- //
- for(auto& rawOutput : rawOutputs)
+ // If we don't have any raw outputs but do have a raw target,
+ // add an empty' rawOutput for certain targets where the expected behavior is obvious.
+ if (m_rawOutputs.getCount() == 0 &&
+ m_rawTargets.getCount() == 1 &&
+ (m_rawTargets[0].format == CodeGenTarget::HostCPPSource ||
+ m_rawTargets[0].format == CodeGenTarget::PyTorchCppBinding ||
+ m_rawTargets[0].format == CodeGenTarget::CUDASource ||
+ ArtifactDescUtil::makeDescForCompileTarget(asExternal(m_rawTargets[0].format)).kind == ArtifactKind::HostCallable))
+ {
+ RawOutput rawOutput;
+ rawOutput.impliedFormat = m_rawTargets[0].format;
+ rawOutput.targetIndex = 0;
+ m_rawOutputs.add(rawOutput);
+ }
+
+ // Consider the output files specified via `-o` and try to figure
+ // out how to deal with them.
+ //
+ for (auto& rawOutput : m_rawOutputs)
+ {
+ // For now, most output formats need to be tightly bound to
+ // both a target and an entry point.
+
+ // If an output doesn't have a target associated with
+ // it, then search for the target with the matching format.
+ if (rawOutput.targetIndex == -1)
{
- // For now, most output formats need to be tightly bound to
- // both a target and an entry point.
+ auto impliedFormat = rawOutput.impliedFormat;
+ int targetIndex = -1;
- // If an output doesn't have a target associated with
- // it, then search for the target with the matching format.
- if( rawOutput.targetIndex == -1 )
+ if (impliedFormat == CodeGenTarget::Unknown)
{
- auto impliedFormat = rawOutput.impliedFormat;
- int targetIndex = -1;
-
- if(impliedFormat == CodeGenTarget::Unknown)
- {
- // If we hit this case, then it means that we need to pick the
- // target to assocaite with this output based on its implied
- // format, but the file path doesn't direclty imply a format
- // (it doesn't have a suffix like `.spv` that tells us what to write).
- //
- sink->diagnose(SourceLoc(), Diagnostics::cannotDeduceOutputFormatFromPath, rawOutput.path);
- }
- else if( mapFormatToTargetIndex.tryGetValue(rawOutput.impliedFormat, targetIndex) )
- {
- rawOutput.targetIndex = targetIndex;
- }
- else
- {
- sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToTarget, rawOutput.path, rawOutput.impliedFormat);
- }
+ // If we hit this case, then it means that we need to pick the
+ // target to assocaite with this output based on its implied
+ // format, but the file path doesn't direclty imply a format
+ // (it doesn't have a suffix like `.spv` that tells us what to write).
+ //
+ m_sink->diagnose(SourceLoc(), Diagnostics::cannotDeduceOutputFormatFromPath, rawOutput.path);
+ }
+ else if (mapFormatToTargetIndex.tryGetValue(rawOutput.impliedFormat, targetIndex))
+ {
+ rawOutput.targetIndex = targetIndex;
}
+ else
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToTarget, rawOutput.path, rawOutput.impliedFormat);
+ }
+ }
- // We won't do any searching to match an output file
- // with an entry point, since the case of a single entry
- // point was handled above, and the user is expected to
- // follow the ordering rules when using multiple entry points.
- if( rawOutput.entryPointIndex == -1 )
+ // We won't do any searching to match an output file
+ // with an entry point, since the case of a single entry
+ // point was handled above, and the user is expected to
+ // follow the ordering rules when using multiple entry points.
+ if (rawOutput.entryPointIndex == -1)
+ {
+ if (rawOutput.targetIndex != -1)
{
- if (rawOutput.targetIndex != -1 )
+ auto outputFormat = m_rawTargets[rawOutput.targetIndex].format;
+ // Here we check whether the given output format supports multiple entry points
+ // When we add targets with support for multiple entry points,
+ // we should update this switch with those new formats
+ switch (outputFormat)
{
- auto outputFormat = rawTargets[rawOutput.targetIndex].format;
- // Here we check whether the given output format supports multiple entry points
- // When we add targets with support for multiple entry points,
- // we should update this switch with those new formats
- switch (outputFormat)
- {
case CodeGenTarget::CPPSource:
case CodeGenTarget::PTX:
case CodeGenTarget::CUDASource:
@@ -2657,91 +2839,139 @@ struct OptionsParser
rawOutput.isWholeProgram = true;
break;
default:
- sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToEntryPoint, rawOutput.path);
+ m_sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToEntryPoint, rawOutput.path);
break;
- }
}
}
}
+ }
- // Now that we've diagnosed the output paths, we can add them
- // to the compile request at the appropriate locations.
- //
- // We will consider the output files specified via `-o` and try to figure
- // out how to deal with them.
- //
- for(auto& rawOutput : rawOutputs)
+ // Now that we've diagnosed the output paths, we can add them
+ // to the compile request at the appropriate locations.
+ //
+ // We will consider the output files specified via `-o` and try to figure
+ // out how to deal with them.
+ //
+ for (auto& rawOutput : m_rawOutputs)
+ {
+ if (rawOutput.targetIndex == -1) continue;
+ auto targetID = m_rawTargets[rawOutput.targetIndex].targetID;
+ auto target = m_requestImpl->getLinkage()->targets[targetID];
+ RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
+ if (!m_requestImpl->m_targetInfos.tryGetValue(target, targetInfo))
{
- if(rawOutput.targetIndex == -1) continue;
- auto targetID = rawTargets[rawOutput.targetIndex].targetID;
- auto target = requestImpl->getLinkage()->targets[targetID];
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if( !requestImpl->m_targetInfos.tryGetValue(target, targetInfo) )
- {
- targetInfo = new EndToEndCompileRequest::TargetInfo();
- requestImpl->m_targetInfos[target] = targetInfo;
- }
+ targetInfo = new EndToEndCompileRequest::TargetInfo();
+ m_requestImpl->m_targetInfos[target] = targetInfo;
+ }
- if (rawOutput.isWholeProgram)
+ if (rawOutput.isWholeProgram)
+ {
+ if (targetInfo->wholeTargetOutputPath != "")
{
- if (targetInfo->wholeTargetOutputPath != "")
- {
- sink->diagnose(SourceLoc(), Diagnostics::duplicateOutputPathsForTarget, target->getTarget());
- }
- else
- {
- target->addTargetFlags(SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM);
- targetInfo->wholeTargetOutputPath = rawOutput.path;
- }
+ m_sink->diagnose(SourceLoc(), Diagnostics::duplicateOutputPathsForTarget, target->getTarget());
}
else
{
- if (rawOutput.entryPointIndex == -1) continue;
+ target->addTargetFlags(SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM);
+ targetInfo->wholeTargetOutputPath = rawOutput.path;
+ }
+ }
+ else
+ {
+ if (rawOutput.entryPointIndex == -1) continue;
- Int entryPointID = rawEntryPoints[rawOutput.entryPointIndex].entryPointID;
- auto entryPointReq = requestImpl->getFrontEndReq()->getEntryPointReqs()[entryPointID];
+ Int entryPointID = m_rawEntryPoints[rawOutput.entryPointIndex].entryPointID;
+ auto entryPointReq = m_requestImpl->getFrontEndReq()->getEntryPointReqs()[entryPointID];
- //String outputPath;
- if (targetInfo->entryPointOutputPaths.containsKey(entryPointID))
- {
- sink->diagnose(SourceLoc(), Diagnostics::duplicateOutputPathsForEntryPointAndTarget, entryPointReq->getName(), target->getTarget());
- }
- else
- {
- targetInfo->entryPointOutputPaths[entryPointID] = rawOutput.path;
- }
+ //String outputPath;
+ if (targetInfo->entryPointOutputPaths.containsKey(entryPointID))
+ {
+ m_sink->diagnose(SourceLoc(), Diagnostics::duplicateOutputPathsForEntryPointAndTarget, entryPointReq->getName(), target->getTarget());
+ }
+ else
+ {
+ targetInfo->entryPointOutputPaths[entryPointID] = rawOutput.path;
}
}
-
- return (sink->getErrorCount() == 0) ? SLANG_OK : SLANG_FAIL;
}
-};
-SlangResult parseOptions(
- SlangCompileRequest* inCompileRequest,
- int argc,
- char const* const* argv)
+ return (m_sink->getErrorCount() == 0) ? SLANG_OK : SLANG_FAIL;
+}
+
+SlangResult OptionsParser::parse(
+ SlangCompileRequest* compileRequest,
+ int argc,
+ char const* const* argv)
{
- Slang::EndToEndCompileRequest* compileRequest = asInternal(inCompileRequest);
+ m_compileRequest = compileRequest;
+
+ // Set up useful members
+ m_requestImpl = asInternal(compileRequest);
- OptionsParser parser;
- parser.compileRequest = inCompileRequest;
- parser.requestImpl = compileRequest;
- parser.session = asInternal(compileRequest->getSession());
+ auto session = asInternal(m_requestImpl->getSession());
+
+ m_session = session;
+ m_frontEndReq = m_requestImpl->getFrontEndReq();
+
+ m_hlslToVulkanLayoutOptions = new HLSLToVulkanLayoutOptions;
+
+ m_cmdOptions = &session->m_commandOptions;
+
+ DiagnosticSink* requestSink = m_requestImpl->getSink();
+
+ m_cmdLineContext = m_requestImpl->getLinkage()->m_downstreamArgs.getContext();
- Result res = parser.parse(argc, argv);
+ // Why create a new DiagnosticSink?
+ // We *don't* want the lexer that comes as default (it's for Slang source!)
+ // We may want to set flags that are different
+ // We will need to use a new sourceManager that will just last for this parse and will map locs to
+ // source lines.
+ //
+ // The *problem* is that we still need to communicate to the requestSink in some suitable way.
+ //
+ // 1) We could have some kind of scoping mechanism (and only one sink)
+ // 2) We could have a 'parent' diagnostic sink, that if we set we route output too
+ // 3) We use something like the ISlangWriter to always be the thing output too (this has problems because
+ // some code assumes the diagnostics are accessible as a string)
+ //
+ // The solution used here is to have DiagnosticsSink have a 'parent' that also gets diagnostics reported to.
+
+ m_parseSink.init(m_cmdLineContext->getSourceManager(), nullptr);
+ {
+ m_parseSink.setFlags(requestSink->getFlags());
+ // Allow HumaneLoc - it won't display much for command line parsing - just (1):
+ // Leaving allows for diagnostics to be compatible with other Slang diagnostic parsing.
+ //parseSink.resetFlag(DiagnosticSink::Flag::HumaneLoc);
+ m_parseSink.setFlag(DiagnosticSink::Flag::SourceLocationLine);
+ }
+
+ // All diagnostics will also be sent to requestSink
+ m_parseSink.setParentSink(requestSink);
+ m_sink = &m_parseSink;
+
+ Result res = _parse(argc, argv);
+
+ m_sink = nullptr;
- DiagnosticSink* sink = compileRequest->getSink();
- if (sink->getErrorCount() > 0)
+ if (requestSink->getErrorCount() > 0)
{
// Put the errors in the diagnostic
- compileRequest->m_diagnosticOutput = sink->outputBuffer.produceString();
+ m_requestImpl->m_diagnosticOutput = requestSink->outputBuffer.produceString();
}
return res;
}
+SlangResult parseOptions(
+ SlangCompileRequest* inCompileRequest,
+ int argc,
+ char const* const* argv)
+{
+ OptionsParser parser;
+ return parser.parse(inCompileRequest, argc, argv);
+}
+
} // namespace Slang
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index 721d4204d..fbd11c7d0 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -332,6 +332,7 @@ struct EntryPointParameterBindingContext
UsedRangeSet usedRangeSet;
};
+
// State that is shared during parameter binding,
// across all translation units
struct SharedParameterBindingContext
@@ -476,7 +477,7 @@ LayoutResourceKind findRegisterClassFromName(UnownedStringSlice const& registerC
break;
case 5:
- if( registerClassName == "space" )
+ if( registerClassName == toSlice("space") )
{
return LayoutResourceKind::RegisterSpace;
}
@@ -963,7 +964,7 @@ static void addExplicitParameterBindings_HLSL(
}
}
-static void maybeDiagnoseMissingVulkanLayoutModifier(
+static void _maybeDiagnoseMissingVulkanLayoutModifier(
ParameterBindingContext* context,
DeclRef<VarDeclBase> const& varDecl)
{
@@ -981,7 +982,6 @@ static void addExplicitParameterBindings_GLSL(
RefPtr<ParameterInfo> parameterInfo,
RefPtr<VarLayout> varLayout)
{
-
// We only want to apply GLSL-style layout modifers
// when compiling for a Khronos-related target.
//
@@ -1002,36 +1002,35 @@ static void addExplicitParameterBindings_GLSL(
//
TypeLayout::ResourceInfo* resInfo = nullptr;
+ TypeLayout::ResourceInfo* foundResInfo = nullptr;
+
LayoutSemanticInfo semanticInfo;
semanticInfo.index = 0;
semanticInfo.space = 0;
- if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) != nullptr )
+
+ if( (foundResInfo = typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) != nullptr )
{
// Try to find `binding` and `set`
- auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>();
- if (!attr)
+ if (auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>())
{
- maybeDiagnoseMissingVulkanLayoutModifier(context, varDecl);
- return;
+ resInfo = foundResInfo;
+ semanticInfo.index = attr->binding;
+ semanticInfo.space = attr->set;
}
- semanticInfo.index = attr->binding;
- semanticInfo.space = attr->set;
}
- else if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) != nullptr )
+ else if( (foundResInfo = typeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) != nullptr )
{
// Try to find `set`
- auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>();
- if (!attr)
+ if (auto attr = varDecl.getDecl()->findModifier<GLSLBindingAttribute>())
{
- maybeDiagnoseMissingVulkanLayoutModifier(context, varDecl);
- return;
- }
- if( attr->binding != 0)
- {
- getSink(context)->diagnose(attr, Diagnostics::wholeSpaceParameterRequiresZeroBinding, varDecl.getName(), attr->binding);
+ resInfo = foundResInfo;
+ if (attr->binding != 0)
+ {
+ getSink(context)->diagnose(attr, Diagnostics::wholeSpaceParameterRequiresZeroBinding, varDecl.getName(), attr->binding);
+ }
+ semanticInfo.index = attr->set;
+ semanticInfo.space = 0;
}
- semanticInfo.index = attr->set;
- semanticInfo.space = 0;
}
else if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::SpecializationConstant)) != nullptr )
{
@@ -1042,15 +1041,64 @@ static void addExplicitParameterBindings_GLSL(
return;
}
- // If we didn't find any matches, then bail
- if(!resInfo)
+ // if we found resInfo, we add the explicit binding
+ if (resInfo)
+ {
+ auto kind = resInfo->kind;
+ auto count = resInfo->count;
+ semanticInfo.kind = kind;
+
+ addExplicitParameterBinding(context, parameterInfo, varDecl, semanticInfo, count);
+ return;
+ }
+
+ // See if we can infer vulkan binding from HLSL if we have such options set
+ auto hlslToVulkanLayoutOptions = context->getTargetRequest()->getHLSLToVulkanLayoutOptions();
+
+ if (!hlslToVulkanLayoutOptions)
+ {
+ _maybeDiagnoseMissingVulkanLayoutModifier(context, varDecl);
+ return;
+ }
+
+ // Do we have any vulkan shift settings
+ auto hlslRegSemantic = varDecl.getDecl()->findModifier<HLSLRegisterSemantic>();
+
+ if (hlslRegSemantic == nullptr)
+ {
+ // We don't have a HLSL binding, so we can't infer, so we can't assign an infered explicit binding
return;
+ }
+
+ // Get the HLSL binding info
+ const auto hlslInfo = ExtractLayoutSemanticInfo(context, hlslRegSemantic);
+ if (hlslInfo.kind != LayoutResourceKind::None)
+ {
+ // We need to map to the GLSL binding types
+ HLSLToVulkanLayoutOptions::Kind vulkanKind = HLSLToVulkanLayoutOptions::getKind(hlslInfo.kind);
+ if (vulkanKind != HLSLToVulkanLayoutOptions::Kind::Invalid)
+ {
+ const auto shift = hlslToVulkanLayoutOptions->getShift(vulkanKind, Index(hlslInfo.space));
+ if (shift != HLSLToVulkanLayoutOptions::kInvalidShift)
+ {
+ const Index bindingIndex = Index(hlslInfo.index) + shift;
- auto kind = resInfo->kind;
- auto count = resInfo->count;
- semanticInfo.kind = kind;
+ if (bindingIndex >= 0)
+ {
+ // Add for descriptor slot
+ resInfo = typeLayout->findOrAddResourceInfo(LayoutResourceKind::DescriptorTableSlot);
+
+ semanticInfo.kind = resInfo->kind;
+ semanticInfo.index = UInt(bindingIndex);
+ semanticInfo.space = hlslInfo.space;
+
+ const LayoutSize count = resInfo->count;
- addExplicitParameterBinding(context, parameterInfo, varDecl, semanticInfo, count);
+ addExplicitParameterBinding(context, parameterInfo, varDecl, semanticInfo, count);
+ }
+ }
+ }
+ }
}
// Given a single parameter, collect whatever information we have on
@@ -2281,7 +2329,7 @@ struct ScopeLayoutBuilder
}
- RefPtr<VarLayout> endLayout()
+ RefPtr<VarLayout> endLayout(VarLayout* inVarLayout = nullptr)
{
// Finish computing the layout for the ordindary data (if any).
//
@@ -2307,7 +2355,12 @@ struct ScopeLayoutBuilder
// We now have a bunch of layout information, which we should
// record into a suitable object that represents the scope
- RefPtr<VarLayout> scopeVarLayout = new VarLayout();
+ RefPtr<VarLayout> scopeVarLayout = inVarLayout;
+ if (!scopeVarLayout)
+ {
+ scopeVarLayout = new VarLayout();
+ }
+
scopeVarLayout->typeLayout = scopeTypeLayout;
if( auto pendingTypeLayout = scopeTypeLayout->pendingDataTypeLayout )
@@ -2445,19 +2498,15 @@ struct SimpleScopeLayoutBuilder : ScopeLayoutBuilder
/// the resources required for a constant buffer in the appropriate
/// target-specific fashion.
///
-static ParameterBindingAndKindInfo maybeAllocateConstantBufferBinding(
- ParameterBindingContext* context,
- bool needConstantBuffer)
+static ParameterBindingAndKindInfo _allocateConstantBufferBinding(
+ ParameterBindingContext* context)
{
- if( !needConstantBuffer ) return ParameterBindingAndKindInfo();
-
UInt space = context->shared->defaultSpace;
auto usedRangeSet = findUsedRangeSetForSpace(context, space);
auto layoutInfo = context->getRulesFamily()
->getConstantBufferRules(context->getTargetRequest())
- ->GetObjectLayout(
- ShaderParameterKind::ConstantBuffer);
+ ->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
ParameterBindingAndKindInfo info;
info.kind = layoutInfo.kind;
@@ -2467,6 +2516,32 @@ static ParameterBindingAndKindInfo maybeAllocateConstantBufferBinding(
return info;
}
+static ParameterBindingAndKindInfo _assignConstantBufferBinding(
+ ParameterBindingContext* context,
+ VarLayout* varLayout,
+ UInt space,
+ UInt index)
+{
+ auto usedRangeSet = findUsedRangeSetForSpace(context, space);
+
+ auto layoutInfo = context->getRulesFamily()
+ ->getConstantBufferRules(context->getTargetRequest())
+ ->GetObjectLayout(ShaderParameterKind::ConstantBuffer);
+
+ const Index count = Index(layoutInfo.size.getFiniteValue());
+
+ auto existingParam = usedRangeSet->usedResourceRanges[(int)layoutInfo.kind].Add(varLayout, index, index + count);
+ SLANG_UNUSED(existingParam);
+ SLANG_ASSERT(existingParam == nullptr);
+
+ ParameterBindingAndKindInfo info;
+ info.kind = layoutInfo.kind;
+ info.count = count;
+ info.index = index;
+ info.space = space;
+ return info;
+}
+
/// Remove resource usage from `typeLayout` that should only be stored per-entry-point.
///
/// This is used when constructing the overall layout for an entry point, to make sure
@@ -3374,6 +3449,92 @@ static void _completeBindings(
_completeBindings(context, program, &counters);
}
+static bool _calcNeedsDefaultSpace(SharedParameterBindingContext& sharedContext)
+{
+ // Next we will look at the global-scope parameters and see if
+ // any of them requires a `register` or `binding` that will
+ // thus need to land in a default space.
+ //
+ for (auto& parameterInfo : sharedContext.parameters)
+ {
+ auto varLayout = parameterInfo->varLayout;
+ SLANG_RELEASE_ASSERT(varLayout);
+
+ // For each parameter, we will look at each resource it consumes.
+ //
+ for (auto resInfo : varLayout->typeLayout->resourceInfos)
+ {
+ // We don't want to consider resource kinds for which
+ // the variable already has an (explicit) binding, since
+ // the space from the explicit binding will be used, so
+ // that a default space isn't needed.
+ //
+ if (parameterInfo->bindingInfo[resInfo.kind].count != 0)
+ continue;
+
+ // We also want to exclude certain resource kinds from
+ // consideration, since parameters using those resource
+ // kinds wouldn't be allocated into the default space
+ // anyway.
+ //
+ switch (resInfo.kind)
+ {
+ case LayoutResourceKind::RegisterSpace:
+ case LayoutResourceKind::PushConstantBuffer:
+ continue;
+ case LayoutResourceKind::Uniform:
+ {
+ // If it's uniform, but we have globals binding defined, we don't need a default space for it
+ // as it will go in the global binding specified
+ if (auto hlslToVulkanOptions = sharedContext.getTargetRequest()->getHLSLToVulkanLayoutOptions())
+ {
+ if (hlslToVulkanOptions->hasGlobalsBinding())
+ {
+ continue;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // Otherwise, we have a shader parameter that will need
+ // a default space or set to live in.
+ //
+ return true;
+ }
+ }
+
+ // We also need a default space for any entry-point parameters
+ // that consume appropriate resource kinds.
+ //
+ for (auto& entryPoint : sharedContext.programLayout->entryPoints)
+ {
+ auto paramsLayout = entryPoint->parametersLayout;
+ for (auto resInfo : paramsLayout->resourceInfos)
+ {
+ switch (resInfo.kind)
+ {
+ default:
+ break;
+
+ case LayoutResourceKind::RegisterSpace:
+ case LayoutResourceKind::VaryingInput:
+ case LayoutResourceKind::VaryingOutput:
+ case LayoutResourceKind::HitAttributes:
+ case LayoutResourceKind::RayPayload:
+ continue;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
RefPtr<ProgramLayout> generateParameterBindings(
TargetProgram* targetProgram,
DiagnosticSink* sink)
@@ -3536,6 +3697,31 @@ RefPtr<ProgramLayout> generateParameterBindings(
}
}
+ // Global constant buffer binding.
+ // It's initially invalid. (kind = None).
+ ParameterBindingAndKindInfo globalConstantBufferBinding;
+
+ // We define this variable early, such that we can create and use when specifying
+ // a HLSLToVulkan based binding. It can if setup be used on endLayout, called later
+ RefPtr<VarLayout> globalScopeVarLayout;
+
+ // If we have a space/binding assigned for use for globals in Vulkan,
+ // we can't use *that* as the default space, so we allocate if
+ if (auto vulkanOptions = targetReq->getHLSLToVulkanLayoutOptions())
+ {
+ if (vulkanOptions->hasGlobalsBinding())
+ {
+ // Create VarLayout which will be associated with the binding, and setup later
+ globalScopeVarLayout = new VarLayout;
+
+ // Allocate the set
+ markSpaceUsed(&context, nullptr, vulkanOptions->m_globalsBindingSet);
+
+ // Mark the use of this binding
+ globalConstantBufferBinding = _assignConstantBufferBinding(&context, globalScopeVarLayout, vulkanOptions->m_globalsBindingSet, vulkanOptions->m_globalsBinding);
+ }
+ }
+
// 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
@@ -3550,10 +3736,14 @@ RefPtr<ProgramLayout> generateParameterBindings(
//
bool needDefaultConstantBuffer = false;
+ // If we have already setup a global constant buffer binding, we don't need a default one
+ //
// On a CPU target, it's okay to have global scope parameters that use Uniform resources (because on CPU
// all resources are 'Uniform')
// TODO(JS): We'll just assume the same with CUDA target for now..
- if (!_isCPUTarget(targetReq->getTarget()) && !_isPTXTarget(targetReq->getTarget()))
+ if (globalConstantBufferBinding.kind == LayoutResourceKind::None &&
+ !_isCPUTarget(targetReq->getTarget()) &&
+ !_isPTXTarget(targetReq->getTarget()))
{
for( auto& parameterInfo : sharedContext.parameters )
{
@@ -3577,81 +3767,9 @@ RefPtr<ProgramLayout> generateParameterBindings(
// As a starting point, we will definitely need a "default" space if
// we are creating a default constant buffer, since it should get
// a binding in that "default" space.
- //
- bool needDefaultSpace = needDefaultConstantBuffer;
- if (!needDefaultSpace)
- {
- // Next we will look at the global-scope parameters and see if
- // any of them requires a `register` or `binding` that will
- // thus need to land in a default space.
- //
- for (auto& parameterInfo : sharedContext.parameters)
- {
- auto varLayout = parameterInfo->varLayout;
- SLANG_RELEASE_ASSERT(varLayout);
-
- // For each parameter, we will look at each resource it consumes.
- //
- for (auto resInfo : varLayout->typeLayout->resourceInfos)
- {
- // We don't want to consider resource kinds for which
- // the variable already has an (explicit) binding, since
- // the space from the explicit binding will be used, so
- // that a default space isn't needed.
- //
- if( parameterInfo->bindingInfo[resInfo.kind].count != 0 )
- continue;
-
- // We also want to exclude certain resource kinds from
- // consideration, since parameters using those resource
- // kinds wouldn't be allocated into the default space
- // anyway.
- //
- switch( resInfo.kind )
- {
- case LayoutResourceKind::RegisterSpace:
- case LayoutResourceKind::PushConstantBuffer:
- continue;
-
- default:
- break;
- }
-
- // Otherwise, we have a shader parameter that will need
- // a default space or set to live in.
- //
- needDefaultSpace = true;
- break;
- }
- }
-
- // We also need a default space for any entry-point parameters
- // that consume appropriate resource kinds.
- //
- for(auto& entryPoint : sharedContext.programLayout->entryPoints)
- {
- auto paramsLayout = entryPoint->parametersLayout;
- for(auto resInfo : paramsLayout->resourceInfos )
- {
- switch(resInfo.kind)
- {
- default:
- break;
-
- case LayoutResourceKind::RegisterSpace:
- case LayoutResourceKind::VaryingInput:
- case LayoutResourceKind::VaryingOutput:
- case LayoutResourceKind::HitAttributes:
- case LayoutResourceKind::RayPayload:
- continue;
- }
-
- needDefaultSpace = true;
- break;
- }
- }
- }
-
+
+ const bool needDefaultSpace = needDefaultConstantBuffer || _calcNeedsDefaultSpace(sharedContext);
+
// If we need a space for default bindings, then allocate it here.
if (needDefaultSpace)
{
@@ -3694,12 +3812,13 @@ RefPtr<ProgramLayout> generateParameterBindings(
}
// If there are any global-scope uniforms, then we need to
- // allocate a constant-buffer binding for them here.
- //
- ParameterBindingAndKindInfo globalConstantBufferBinding = maybeAllocateConstantBufferBinding(
- &context,
- needDefaultConstantBuffer);
-
+ // allocate a constant-buffer binding for them here, if hasn't already been
+ // assigned
+ if (globalConstantBufferBinding.kind == LayoutResourceKind::None && needDefaultConstantBuffer)
+ {
+ globalConstantBufferBinding = _allocateConstantBufferBinding(&context);
+ }
+
// Now that all of the explicit bindings have been dealt with
// and we've also allocate any space/buffer that is required
// for global-scope parameters, we will go through the
@@ -3721,10 +3840,12 @@ RefPtr<ProgramLayout> generateParameterBindings(
globalScopeLayoutBuilder.addParameter(parameterInfo);
}
- auto globalScopeVarLayout = globalScopeLayoutBuilder.endLayout();
+ globalScopeVarLayout = globalScopeLayoutBuilder.endLayout(globalScopeVarLayout);
+
if( globalConstantBufferBinding.count != 0 )
{
auto cbInfo = globalScopeVarLayout->findOrAddResourceInfo(globalConstantBufferBinding.kind);
+
cbInfo->space = globalConstantBufferBinding.space;
cbInfo->index = globalConstantBufferBinding.index;
}
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index ec5a7084e..a1c8f9b03 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -61,6 +61,7 @@ extern Slang::String get_slang_hlsl_prelude();
namespace Slang {
+
/* static */const BaseTypeInfo BaseTypeInfo::s_info[Index(BaseType::CountOf)] =
{
{ 0, 0, uint8_t(BaseType::Void) },
@@ -1444,6 +1445,14 @@ MatrixLayoutMode TargetRequest::getDefaultMatrixLayoutMode()
return linkage->getDefaultMatrixLayoutMode();
}
+void TargetRequest::setHLSLToVulkanLayoutOptions(HLSLToVulkanLayoutOptions* opts)
+{
+ if (isKhronosTarget(this))
+ {
+ hlslToVulkanLayoutOptions = opts;
+ }
+}
+
void TargetRequest::addCapability(CapabilityAtom capability)
{
rawCapabilities.add(capability);
@@ -4678,6 +4687,11 @@ void EndToEndCompileRequest::setTargetProfile(int targetIndex, SlangProfileID pr
getLinkage()->targets[targetIndex]->setTargetProfile(Profile(profile));
}
+void EndToEndCompileRequest::setHLSLToVulkanLayoutOptions(int targetIndex, HLSLToVulkanLayoutOptions* vulkanShiftOptions)
+{
+ getLinkage()->targets[targetIndex]->setHLSLToVulkanLayoutOptions(vulkanShiftOptions);
+}
+
void EndToEndCompileRequest::setTargetFlags(int targetIndex, SlangTargetFlags flags)
{
getLinkage()->targets[targetIndex]->addTargetFlags(flags);
diff --git a/tests/bindings/hlsl-to-vulkan-combined.hlsl b/tests/bindings/hlsl-to-vulkan-combined.hlsl
new file mode 100644
index 000000000..20668b01b
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-combined.hlsl
@@ -0,0 +1,9 @@
+//TEST:REFLECTION:-target glsl -profile ps_4_0 -entry main -fvk-t-shift 5 all -fvk-t-shift 7 2 -fvk-s-shift -3 0 -fvk-b-shift 1 2
+
+Sampler2D<float> t0 : register(t2);
+Sampler2D<float4> t1 : register(t7, space2);
+
+float4 main() : SV_TARGET
+{
+ return float4(1, 1, 1, 0);
+} \ No newline at end of file
diff --git a/tests/bindings/hlsl-to-vulkan-combined.hlsl.expected b/tests/bindings/hlsl-to-vulkan-combined.hlsl.expected
new file mode 100644
index 000000000..127d0fe4c
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-combined.hlsl.expected
@@ -0,0 +1,54 @@
+result code = 0
+standard error = {
+}
+standard output = {
+{
+ "parameters": [
+ {
+ "name": "t0",
+ "binding": {"kind": "descriptorTableSlot", "index": 7},
+ "type": {
+ "kind": "resource",
+ "baseShape": "texture2D"
+ }
+ },
+ {
+ "name": "t1",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 14},
+ "type": {
+ "kind": "resource",
+ "baseShape": "texture2D"
+ }
+ }
+ ],
+ "entryPoints": [
+ {
+ "name": "main",
+ "stage:": "fragment",
+ "result:": {
+ "stage": "fragment",
+ "binding": {"kind": "varyingOutput", "index": 0},
+ "semanticName": "SV_TARGET",
+ "type": {
+ "kind": "vector",
+ "elementCount": 4,
+ "elementType": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ }
+ }
+ },
+ "bindings": [
+ {
+ "name": "t0",
+ "binding": {"kind": "descriptorTableSlot", "index": 7}
+ },
+ {
+ "name": "t1",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 14}
+ }
+ ]
+ }
+ ]
+}
+}
diff --git a/tests/bindings/hlsl-to-vulkan-global.hlsl b/tests/bindings/hlsl-to-vulkan-global.hlsl
new file mode 100644
index 000000000..aceec02ba
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-global.hlsl
@@ -0,0 +1,12 @@
+//TEST:REFLECTION:-target glsl -profile ps_4_0 -entry main -fvk-bind-globals 5 9
+
+uniform int a;
+uniform float b;
+
+Texture2D t;
+SamplerState sampler;
+
+float4 main() : SV_TARGET
+{
+ return t.SampleLevel(sampler, float2(a,b), 0) + float4(a, b, 1, 0);
+} \ No newline at end of file
diff --git a/tests/bindings/hlsl-to-vulkan-global.hlsl.expected b/tests/bindings/hlsl-to-vulkan-global.hlsl.expected
new file mode 100644
index 000000000..b0b8f6a17
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-global.hlsl.expected
@@ -0,0 +1,77 @@
+result code = 0
+standard error = {
+}
+standard output = {
+{
+ "parameters": [
+ {
+ "name": "a",
+ "binding": {"kind": "uniform", "offset": 0, "size": 4},
+ "type": {
+ "kind": "scalar",
+ "scalarType": "int32"
+ }
+ },
+ {
+ "name": "b",
+ "binding": {"kind": "uniform", "offset": 4, "size": 4},
+ "type": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ }
+ },
+ {
+ "name": "t",
+ "binding": {"kind": "descriptorTableSlot", "index": 0},
+ "type": {
+ "kind": "resource",
+ "baseShape": "texture2D"
+ }
+ },
+ {
+ "name": "sampler",
+ "binding": {"kind": "descriptorTableSlot", "index": 1},
+ "type": {
+ "kind": "samplerState"
+ }
+ }
+ ],
+ "entryPoints": [
+ {
+ "name": "main",
+ "stage:": "fragment",
+ "result:": {
+ "stage": "fragment",
+ "binding": {"kind": "varyingOutput", "index": 0},
+ "semanticName": "SV_TARGET",
+ "type": {
+ "kind": "vector",
+ "elementCount": 4,
+ "elementType": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ }
+ }
+ },
+ "bindings": [
+ {
+ "name": "a",
+ "binding": {"kind": "uniform", "offset": 0, "size": 4}
+ },
+ {
+ "name": "b",
+ "binding": {"kind": "uniform", "offset": 4, "size": 4}
+ },
+ {
+ "name": "t",
+ "binding": {"kind": "descriptorTableSlot", "index": 0}
+ },
+ {
+ "name": "sampler",
+ "binding": {"kind": "descriptorTableSlot", "index": 1}
+ }
+ ]
+ }
+ ]
+}
+}
diff --git a/tests/bindings/hlsl-to-vulkan-shift.hlsl b/tests/bindings/hlsl-to-vulkan-shift.hlsl
new file mode 100644
index 000000000..8981b6be6
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-shift.hlsl
@@ -0,0 +1,21 @@
+//TEST:REFLECTION:-target glsl -profile ps_4_0 -entry main -fvk-t-shift 5 all -fvk-t-shift 7 2 -fvk-s-shift -3 0 -fvk-b-shift 1 2
+
+struct Data
+{
+ float a;
+ int b;
+};
+
+Texture2D t : register(t0);
+SamplerState s : register(s4);
+ConstantBuffer<Data> c : register(b2);
+
+Texture2D t2 : register(t0, space2);
+
+RWStructuredBuffer<Data> u : register(u11);
+RWStructuredBuffer<int> u2 : register(u3, space2);
+
+float4 main() : SV_TARGET
+{
+ return float4(1, 1, 1, 0);
+} \ No newline at end of file
diff --git a/tests/bindings/hlsl-to-vulkan-shift.hlsl.expected b/tests/bindings/hlsl-to-vulkan-shift.hlsl.expected
new file mode 100644
index 000000000..b0d8ace6f
--- /dev/null
+++ b/tests/bindings/hlsl-to-vulkan-shift.hlsl.expected
@@ -0,0 +1,178 @@
+result code = 0
+standard error = {
+}
+standard output = {
+{
+ "parameters": [
+ {
+ "name": "t",
+ "binding": {"kind": "descriptorTableSlot", "index": 5},
+ "type": {
+ "kind": "resource",
+ "baseShape": "texture2D"
+ }
+ },
+ {
+ "name": "s",
+ "binding": {"kind": "descriptorTableSlot", "index": 1},
+ "type": {
+ "kind": "samplerState"
+ }
+ },
+ {
+ "name": "c",
+ "binding": {"kind": "descriptorTableSlot", "index": 0},
+ "type": {
+ "kind": "constantBuffer",
+ "elementType": {
+ "kind": "struct",
+ "name": "Data",
+ "fields": [
+ {
+ "name": "a",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ },
+ "binding": {"kind": "uniform", "offset": 0, "size": 4}
+ },
+ {
+ "name": "b",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "int32"
+ },
+ "binding": {"kind": "uniform", "offset": 4, "size": 4}
+ }
+ ]
+ },
+ "containerVarLayout": {
+ "binding": {"kind": "descriptorTableSlot", "index": 0}
+ },
+ "elementVarLayout": {
+ "type": {
+ "kind": "struct",
+ "name": "Data",
+ "fields": [
+ {
+ "name": "a",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ },
+ "binding": {"kind": "uniform", "offset": 0, "size": 4}
+ },
+ {
+ "name": "b",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "int32"
+ },
+ "binding": {"kind": "uniform", "offset": 4, "size": 4}
+ }
+ ]
+ },
+ "binding": {"kind": "uniform", "offset": 0, "size": 16}
+ }
+ }
+ },
+ {
+ "name": "t2",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 7},
+ "type": {
+ "kind": "resource",
+ "baseShape": "texture2D"
+ }
+ },
+ {
+ "name": "u",
+ "binding": {"kind": "descriptorTableSlot", "index": 2},
+ "type": {
+ "kind": "resource",
+ "baseShape": "structuredBuffer",
+ "access": "readWrite",
+ "resultType": {
+ "kind": "struct",
+ "name": "Data",
+ "fields": [
+ {
+ "name": "a",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ },
+ "binding": {"kind": "uniform", "offset": 0, "size": 4}
+ },
+ {
+ "name": "b",
+ "type": {
+ "kind": "scalar",
+ "scalarType": "int32"
+ },
+ "binding": {"kind": "uniform", "offset": 4, "size": 4}
+ }
+ ]
+ }
+ }
+ },
+ {
+ "name": "u2",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 4},
+ "type": {
+ "kind": "resource",
+ "baseShape": "structuredBuffer",
+ "access": "readWrite",
+ "resultType": {
+ "kind": "scalar",
+ "scalarType": "int32"
+ }
+ }
+ }
+ ],
+ "entryPoints": [
+ {
+ "name": "main",
+ "stage:": "fragment",
+ "result:": {
+ "stage": "fragment",
+ "binding": {"kind": "varyingOutput", "index": 0},
+ "semanticName": "SV_TARGET",
+ "type": {
+ "kind": "vector",
+ "elementCount": 4,
+ "elementType": {
+ "kind": "scalar",
+ "scalarType": "float32"
+ }
+ }
+ },
+ "bindings": [
+ {
+ "name": "t",
+ "binding": {"kind": "descriptorTableSlot", "index": 5}
+ },
+ {
+ "name": "s",
+ "binding": {"kind": "descriptorTableSlot", "index": 1}
+ },
+ {
+ "name": "c",
+ "binding": {"kind": "descriptorTableSlot", "index": 0}
+ },
+ {
+ "name": "t2",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 7}
+ },
+ {
+ "name": "u",
+ "binding": {"kind": "descriptorTableSlot", "index": 2}
+ },
+ {
+ "name": "u2",
+ "binding": {"kind": "descriptorTableSlot", "space": 2, "index": 4}
+ }
+ ]
+ }
+ ]
+}
+}