summaryrefslogtreecommitdiffstats
path: root/source
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 /source
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.
Diffstat (limited to 'source')
-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
9 files changed, 2383 insertions, 1791 deletions
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);