diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-04-29 09:24:26 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-29 09:24:26 -0400 |
| commit | 19c0866b050a022406867aa650302f4efbf8e010 (patch) | |
| tree | f5ed4e1f5d27865518daf81c7e861b4908186b23 /source/slang | |
| parent | c571bcb025009f9c662e8d631fa49dbfed560287 (diff) | |
CommandOptions (#2856)
* WIP CommandOptions
* Fix some output issues.
* Simplify word wrapping.
* Add file extensions.
* Change how lookup takes place.
Add appendSplit functions to StringUtil.
Make Categories hold the index range of their options.
* Small improvement.
* Lookup with partial option names.
* Associate user values.
* Encoding flags in the name.
* Refactor setting up of command options.
* Use CommandOptions in slang-options.
* Remove old help text.
* Cache the CommandOptions on the Session.
* Range checking.
Fix bug in the Options handling.
* Extra checks for validity.
* Get categories directly.
* Slight improvements over output.
* Added NameValue types.
* Fix typo.
Remove some now unused diagnostics.
Fix diagnostic in testing, as output has changed.
* Add minimal usage message.
* Remove platform executable extension from diagnostics output.
* Some improvements around getting names from NameValue types.
* Improve some option descriptions.
* Small fixes.
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/slang-capability.cpp | 9 | ||||
| -rw-r--r-- | source/slang/slang-capability.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 13 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 1223 | ||||
| -rw-r--r-- | source/slang/slang-options.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-profile.h | 8 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 4 |
9 files changed, 795 insertions, 479 deletions
diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp index 770685247..28dea8d23 100644 --- a/source/slang/slang-capability.cpp +++ b/source/slang/slang-capability.cpp @@ -117,6 +117,15 @@ static CapabilityAtomInfo const& _getInfo(CapabilityAtom atom) return kCapabilityAtoms[Int(atom)]; } +void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames) +{ + ioNames.setCount(Count(CapabilityAtom::Count)); + for (Index i = 0; i < Count(CapabilityAtom::Count); ++i) + { + ioNames[i] = UnownedStringSlice(_getInfo(CapabilityAtom(i)).name); + } +} + CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name) { // For now we are implementing a linear search over the diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h index 90b03ed29..55c5044ed 100644 --- a/source/slang/slang-capability.h +++ b/source/slang/slang-capability.h @@ -161,4 +161,8 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base); /// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid. CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name); + + /// Gets the capability names. +void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames); + } diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 0aa7e48a9..4ca76c53a 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -372,13 +372,9 @@ namespace Slang } } - static const struct + static const StageInfo kStages[] = { - char const* name; - Stage stage; - } kStages[] = - { - #define PROFILE_STAGE(ID, NAME, ENUM) \ + #define PROFILE_STAGE(ID, NAME, ENUM) \ { #NAME, Stage::ID }, #define PROFILE_STAGE_ALIAS(ID, NAME, VAL) \ @@ -387,6 +383,11 @@ namespace Slang #include "slang-profile-defs.h" }; + ConstArrayView<StageInfo> getStageInfos() + { + return makeConstArrayView(kStages); + } + Stage findStageByName(String const& name) { for(auto entry : kStages) diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 83acbdd9d..fc46b25b6 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -13,6 +13,7 @@ #include "../compiler-core/slang-command-line-args.h" #include "../core/slang-std-writers.h" +#include "../core/slang-command-options.h" #include "../../slang-com-ptr.h" @@ -3005,6 +3006,9 @@ namespace Slang DownstreamCompilerLocatorFunc m_downstreamCompilerLocators[int(PassThroughMode::CountOf)]; Name* m_completionTokenName = nullptr; ///< The name of a completion request token. + /// For parsing command line options + CommandOptions m_commandOptions; + private: void _initCodeGenTransitionMap(); diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index cb441ade8..8738c71cc 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -76,8 +76,6 @@ DIAGNOSTIC( 19, Error, unknownSourceLanguage, "unknown source language '$0'") DIAGNOSTIC( 20, Error, entryPointsNeedToBeAssociatedWithTranslationUnits, "when using multiple source files, entry points must be specified after their corresponding source file(s)") DIAGNOSTIC( 22, Error, unknownDownstreamCompiler, "unknown downstream compiler '$0'") -DIAGNOSTIC( 24, Error, unknownLineDirectiveMode, "unknown '#line' directive mode '$0'") -DIAGNOSTIC( 25, Error, unknownFloatingPointMode, "unknown floating-point mode '$0'") DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'") DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'") @@ -104,6 +102,9 @@ DIAGNOSTIC( 50, Error, duplicateTargets, "the target '$0' has been specified DIAGNOSTIC( 60, Error, cannotDeduceOutputFormatFromPath, "cannot infer an output format from the output path '$0'") DIAGNOSTIC( 61, Error, cannotMatchOutputFileToTarget, "no specified '-target' option matches the output path '$0', which implies the '$1' format") +DIAGNOSTIC( 62, Error, unknownCommandLineValue, "unknown value for option. Valid values are '$0'") +DIAGNOSTIC( 63, Error, unknownHelpCategory, "unknown help category") + DIAGNOSTIC( 70, Error, cannotMatchOutputFileToEntryPoint, "the output path '$0' is not associated with any entry point; a '-o' option for a compiled kernel must follow the '-entry' option for its corresponding entry point") DIAGNOSTIC( 80, Error, duplicateOutputPathsForEntryPointAndTarget, "multiple output paths have been specified entry point '$0' on target '$1'") diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 8817095df..6cb53496f 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -29,12 +29,494 @@ #include "../compiler-core/slang-artifact-desc-util.h" #include "../compiler-core/slang-core-diagnostics.h" +#include "../core/slang-string-slice-pool.h" #include "../core/slang-char-util.h" +#include "../core/slang-command-options.h" + #include <assert.h> namespace Slang { +namespace { // anonymous + +// All of the options are given an unique enum +enum class OptionKind +{ + // General + + MacroDefine, + DepFile, + EntryPointName, + Help, + Include, + Language, + MatrixLayoutColumn, + MatrixLayoutRow, + ModuleName, + Output, + Profile, + Stage, + Target, + Version, + WarningsAsErrors, + DisableWarnings, + EnableWarning, + DisableWarning, + DumpWarningDiagnostics, + InputFilesRemain, + EmitIr, + + // Target + + Capability, + DefaultImageFormatUnknown, + DisableDynamicDispatch, + DisableSpecialization, + FloatingPointMode, + DebugInformation, + LineDirectiveMode, + Optimization, + Obfuscate, + + // Downstream + + CompilerPath, + DefaultDownstreamCompiler, + DownstreamArgs, + PassThrough, + + // Debugging + + DumpAst, + DumpIntermediatePrefix, + DumpIntermediates, + DumpIr, + DumpIrIds, + DumpRepro, + DumpReproOnError, + PreprocessorOutput, + ExtractRepro, + LoadRepro, + LoadReproDirectory, + NoCodeGen, + OutputIncludes, + ReproFileSystem, + SerialIr, + SkipCodeGen, + ValidateIr, + VerbosePaths, + VerifyDebugSerialIr, + + // Experimental + + EmitSpirvDirectly, + FileSystem, + Heterogeneous, + NoMangle, + + // Internal + + ArchiveType, + CompileStdLib, + Doc, + IrCompression, + LoadStdLib, + ReferenceModule, + SaveStdLib, + SaveStdLibBinSource, + TrackLiveness, + + // Depreciated + ParameterBlocksUseRegisterSpaces, + + CountOf, +}; + +struct Option +{ + OptionKind optionKind; + const char* name; + const char* usage = nullptr; + const char* description = nullptr; +}; + +enum class ValueCategory +{ + Compiler, + Target, + Language, + FloatingPointMode, + ArchiveType, + Stage, + LineDirectiveMode, + DebugInfoFormat, + + CountOf, +}; + +} // anonymous + +static void _addOptions(const ConstArrayView<Option>& options, CommandOptions& cmdOptions) +{ + for (auto& opt : options) + { + cmdOptions.add(opt.name, opt.usage, opt.description, CommandOptions::UserValue(opt.optionKind)); + } +} + +void initCommandOptions(CommandOptions& options) +{ + typedef CommandOptions::Flag::Enum Flag; + typedef CommandOptions::CategoryKind CategoryKind; + typedef CommandOptions::UserValue UserValue; + + // Add all the option categories + + options.addCategory(CategoryKind::Option, "General", "General options"); + options.addCategory(CategoryKind::Option, "Target", "Target code generation options"); + options.addCategory(CategoryKind::Option, "Downstream", "Downstream compiler options"); + options.addCategory(CategoryKind::Option, "Debugging", "Compiler debugging/instrumentation options"); + options.addCategory(CategoryKind::Option, "Experimental", "Experimental options (use at your own risk)"); + options.addCategory(CategoryKind::Option, "Internal", "Internal-use options (use at your own risk)"); + options.addCategory(CategoryKind::Option, "Depreciated", "Deprecated options (allowed but ignored; may be removed in future)"); + + // Do the easy ones + { + options.addCategory(CategoryKind::Value, "compiler", "Downstream Compilers (aka Pass through)", UserValue(ValueCategory::Compiler)); + options.addValues(TypeTextUtil::getCompilerInfos()); + + options.addCategory(CategoryKind::Value, "language", "Language", UserValue(ValueCategory::Language)); + options.addValues(TypeTextUtil::getLanguageInfos()); + + options.addCategory(CategoryKind::Value, "archive-type", "Archive Type", UserValue(ValueCategory::ArchiveType)); + options.addValues(TypeTextUtil::getArchiveTypeInfos()); + + options.addCategory(CategoryKind::Value, "line-directive-mode", "Line Directive Mode", UserValue(ValueCategory::LineDirectiveMode)); + options.addValues(TypeTextUtil::getLineDirectiveInfos()); + + options.addCategory(CategoryKind::Value, "debug-info-format", "Debug Info Format", UserValue(ValueCategory::DebugInfoFormat)); + options.addValues(TypeTextUtil::getDebugInfoFormatInfos()); + + options.addCategory(CategoryKind::Value, "fp-mode", "Floating Point Mode", UserValue(ValueCategory::FloatingPointMode)); + options.addValues(TypeTextUtil::getFloatingPointModeInfos()); + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + { + options.addCategory(CategoryKind::Value, "target", "Target", UserValue(ValueCategory::Target)); + for (auto opt : TypeTextUtil::getCompileTargetInfos()) + { + options.addValue(opt.names, opt.description, UserValue(opt.target)); + } + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stage !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + { + options.addCategory(CategoryKind::Value, "stage", "Stage", UserValue(ValueCategory::Stage)); + List<NameValue> opts; + for (auto& info: getStageInfos()) + { + opts.add({ValueInt(info.stage), info.name }); + } + options.addValuesWithAliases(opts.getArrayView()); + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! capabilities !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + { + options.addCategory(CategoryKind::Value, "capability", + "A capability describes an optional feature that a target may or " + "may not support. When a -capability is specified, the compiler " + "may assume that the target supports that capability, and generate " + "code accordingly."); + + List<UnownedStringSlice> names; + getCapabilityAtomNames(names); + + // We'll just add to keep the list more simple... + options.addValue("spirv_1_{ 0,1,2,3,4,5 }", "minimum supported SPIR - V version"); + + for (auto name : names) + { + if (name.startsWith("__") || + name.startsWith("spirv_1_")) + { + continue; + } + else if (name.startsWith("GL_")) + { + // We'll assume it is an extension.. + StringBuilder buf; + buf << "enables the " << name << " extension"; + options.addValue(name, buf.getUnownedSlice()); + } + else + { + options.addValue(name); + } + } + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! extension !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + { + options.addCategory(CategoryKind::Value, "file-extension", + "A <language>, <format>, and/or <stage> may be inferred from the " + "extension of an input or -o path"); + + // TODO(JS): It's concevable that these are enumerated via some other system + // rather than just being listed here + + const CommandOptions::ValuePair pairs[] = + { + {"hlsl,fx", "hlsl"}, + {"dxbc"}, + {"dxbc-asm", "dxbc-assembly"}, + {"dxil"}, + {"dxil-asm", "dxil-assembly"}, + {"glsl"}, + {"vert", "glsl (vertex)"}, + {"frag", "glsl (fragment)"}, + {"geom", "glsl (geoemtry)"}, + {"tesc", "glsl (hull)"}, + {"tese", "glsl (domain)"}, + {"comp", "glsl (compute)"}, + {"slang"}, + {"spv", "SPIR-V"}, + {"spv-asm", "SPIR-V assembly"}, + {"c"}, + {"cpp,c++,cxx", "C++"}, + {"exe", "executable"}, + {"dll,so", "sharedlibrary/dll"}, + {"cu", "CUDA"}, + {"ptx", "PTX"}, + {"obj,o", "object-code"}, + {"zip", "container"}, + {"slang-module,slang-library", "Slang Module/Library"}, + {"dir", "Container as a directory"}, + }; + options.addValues(pairs, SLANG_COUNT_OF(pairs)); + } + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! General !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("General"); + + const Option generalOpts[] = + { + { OptionKind::MacroDefine, "-D?...", "-D<name>[=<value>], -D <name>[=<value>]", "Insert a preprocessor macro." }, + { OptionKind::DepFile, "-depfile", "-depfile <path>", "Save the source file dependency list in a file." }, + { OptionKind::EntryPointName, "-entry", "-entry <name>", + "Specify the name of an entry-point function.\n" + "Multiple -entry options may be used in a single invocation. " + "If no -entry options are given, compiler will use [shader(...)] " + "attributes to detect entry points."}, + { OptionKind::EmitIr, "-emit-ir", nullptr, "Emit IR typically as a '.slang-module' when outputting to a container." }, + { OptionKind::Help, "-h,-help,--help", "-h or -h <help-category>", "Print this message, or help in specified category." }, + { OptionKind::Include, "-I?...", "-I<path>, -I <path>", + "Add a path to be used in resolving '#include' " + "and 'import' operations."}, + { OptionKind::Language, "-lang", "-lang <language>", "Set the language for the following input files."}, + { OptionKind::MatrixLayoutColumn, "-matrix-layout-column-major", nullptr, "Set the default matrix layout to column-major."}, + { OptionKind::MatrixLayoutRow,"-matrix-layout-row-major", nullptr, "Set the default matrix layout to row-major."}, + { OptionKind::ModuleName, "-module-name", "-module-name <name>", + "Set the module name to use when compiling multiple .slang source files into a single module."}, + { OptionKind::Output, "-o", "-o <path>", + "Specify a path where generated output should be written.\n" + "If no -target or -stage is specified, one may be inferred " + "from file extension (see <file-extension>). " + "If multiple -target options and a single -entry are present, each -o " + "associates with the first -target to its left. " + "Otherwise, if multiple -entry options are present, each -o associates " + "with the first -entry to its left, and with the -target that matches " + "the one inferred from <path>."}, + { OptionKind::Profile, "-profile", "-profile <profile>[+<capability>...]", + "Specify the shader profile for code generation.\n" + "Accepted profiles are:\n" + " sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n" + " glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n" + "Additional profiles that include -stage information:\n" + " {vs,hs,ds,gs,ps}_<version>\n" + "See -capability for information on <capability>\n" + "When multiple -target options are present, each -profile associates " + "with the first -target to its left."}, + { OptionKind::Stage, "-stage", "-stage <stage>", + "Specify the stage of an entry-point function.\n" + "When multiple -entry options are present, each -stage associated with " + "the first -entry to its left.\n" + "May be omitted if entry-point function has a [shader(...)] attribute; " + "otherwise required for each -entry option."}, + { OptionKind::Target, "-target", "-target <target>", "Specifies the format in which code should be generated."}, + { OptionKind::Version, "-v,-version", nullptr, "Display the build version."}, + { OptionKind::WarningsAsErrors, "-warnings-as-errors", "-warnings-as-errors all or -warnings-as-errors <id>[,<id>...]", + "all - Treat all warnings as errors.\n" + "<id>[,<id>...]: Treat specific warning ids as errors.\n"}, + { OptionKind::DisableWarnings, "-warnings-disable", "-warnings-disable <id>[,<id>...]", "Disable specific warning ids."}, + { OptionKind::EnableWarning, "-W...", "-W<id>", "Enable a warning with the specified id."}, + { OptionKind::DisableWarning, "-Wno-...", "-Wno-<id>", "Disable warning with <id>"}, + { OptionKind::DumpWarningDiagnostics, "-dump-warning-diagnostics", nullptr, "Dump to output list of warning diagnostic numeric and name ids." }, + { OptionKind::InputFilesRemain, "--", nullptr, "Treat the rest of the command line as input files."} + }; + + _addOptions(makeConstArrayView(generalOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Target !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Target"); + + const Option targetOpts[] = + { + { OptionKind::Capability, "-capability", "-capability <capability>[+<capability>...]", + "Add optional capabilities to a code generation target. See Capabilities below."}, + { OptionKind::DefaultImageFormatUnknown, "-default-image-format-unknown", nullptr, + "Set the format of R/W images with unspecified format to 'unknown'. Otherwise try to guess the format."}, + { OptionKind::DisableDynamicDispatch, "-disable-dynamic-dispatch", nullptr, "Disables generating dynamic dispatch code." }, + { OptionKind::DisableSpecialization, "-disable-specialization", nullptr, "Disables generics and specialization pass." }, + { OptionKind::FloatingPointMode, "-fp-mode,-floating-point-mode", "-fp-mode <fp-mode>, -floating-point-mode <fp-mode>", + "Control floating point optimizations"}, + { OptionKind::DebugInformation, "-g...", "-g, -g<N>, -g<debug-info-format>", + "Include debug information in the generated code, where possible.\n" + "N is the amount of information, 0..3, unspecified means 2\n" + "<debug-info-format> specifies a debugging info format\n" + "It is valid to have multiple -g options, such as a level and a <debug-info-format>" }, + { OptionKind::LineDirectiveMode, "-line-directive-mode", "-line-directive-mode <line-directive-mode>", + "Sets how the `#line` directives should be produced. Available options are:\n" + "If not specified, default behavior is to use C-style `#line` directives " + "for HLSL and C/C++ output, and traditional GLSL-style `#line` directives " + "for GLSL output." }, + { OptionKind::Optimization, "-O...", "-O<N>", + "Set the optimization level.\n" + "N is the amount of optimization, 0..3, default is 1" }, + { OptionKind::Obfuscate, "-obfuscate", nullptr, "Remove all source file information from outputs." }, + }; + + _addOptions(makeConstArrayView(targetOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Downstream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Downstream"); + + { + auto namesList = NameValueUtil::getNames(NameValueUtil::NameKind::First, TypeTextUtil::getCompilerInfos()); + StringBuilder names; + for (auto name : namesList) + { + names << "-" << name << "-path,"; + } + // remove last , + names.reduceLength(names.getLength() - 1); + + options.add(names.getBuffer(), "-<compiler>-path <path>", + "Specify path to a downstream <compiler> " + "executable or library.\n", + UserValue(OptionKind::CompilerPath)); + } + + const Option downstreamOpts[] = + { + { OptionKind::DefaultDownstreamCompiler, "-default-downstream-compiler", "-default-downstream-compiler <language> <compiler>", + "Set a default compiler for the given language. See -lang for the list of languages." }, + { OptionKind::DownstreamArgs, "-X...", "-X<compiler> <option> -X<compiler>... <options> -X.", + "Pass arguments to downstream <compiler>. Just -X<compiler> passes just the next argument " + "to the downstream compiler. -X<compiler>... options -X. will pass *all* of the options " + "inbetween the opening -X and -X. to the downstream compiler."}, + { OptionKind::PassThrough, "-pass-through", "-pass-through <compiler>", + "Pass the input through mostly unmodified to the \n" + "existing compiler <compiler>." }, + }; + + _addOptions(makeConstArrayView(downstreamOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Debugging !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Debugging"); + + const Option debuggingOpts[] = + { + { OptionKind::DumpAst, "-dump-ast", nullptr, "Dump the AST to a .slang-ast file next to the input." }, + { OptionKind::DumpIntermediatePrefix, "-dump-intermediate-prefix", "-dump-intermediate-prefix <prefix>", + "File name prefix for -dump-intermediates outputs, default is 'slang-dump-'"}, + { OptionKind::DumpIntermediates, "-dump-intermediates", nullptr, "Dump intermediate outputs for debugging." }, + { OptionKind::DumpIr, "-dump-ir", nullptr, "Dump the IR for debugging." }, + { OptionKind::DumpIrIds, "-dump-ir-ids", nullptr, "Dump the IDs with -dump-ir (debug builds only)" }, + { OptionKind::DumpRepro, "-dump-repro", nullptr, "Dump a `.slang-repro` file that can be used to reproduce " + "a compilation on another machine.\n"}, + { OptionKind::DumpReproOnError, "-dump-repro-on-error", nullptr, "Dump `.slang-repro` file on any compilation error." }, + { OptionKind::PreprocessorOutput, "-E,-output-preprocessor", nullptr, "Output the preprocessing result and exit." }, + { OptionKind::ExtractRepro, "-extract-repro", "-extract-repro <name>", "Extract the repro files into a folder." }, + { OptionKind::LoadReproDirectory, "-load-repro-directory", "-load-repro-directory <path>", "Use repro along specified path" }, + { OptionKind::LoadRepro, "-load-repro", "-load-repro <name>", "Load repro"}, + { OptionKind::NoCodeGen, "-no-codegen", nullptr, "Skip the code generation step, just check the code and generate layout." }, + { OptionKind::OutputIncludes, "-output-includes", nullptr, "Print the hierarchy of the processed source files." }, + { OptionKind::ReproFileSystem, "-repro-file-system", "-repro-file-system <name>", "Use a repro as a file system" }, + { OptionKind::SerialIr, "-serial-ir", nullptr, "Serialize the IR between front-end and back-end." }, + { OptionKind::SkipCodeGen, "-skip-codegen", nullptr, "Skip the code generation phase." }, + { OptionKind::ValidateIr, "-validate-ir", nullptr, "Validate the IR between the phases." }, + { OptionKind::VerbosePaths, "-verbose-paths", nullptr, "When displaying diagnostic output aim to display more detailed path information. " + "In practice this is typically the complete 'canonical' path to the source file used." }, + { OptionKind::VerifyDebugSerialIr, "-verify-debug-serial-ir", nullptr, "Verify IR in the front-end." } + }; + _addOptions(makeConstArrayView(debuggingOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Experimental !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Experimental"); + + const Option experimentalOpts[] = + { + { OptionKind::EmitSpirvDirectly, "-emit-spirv-directly", nullptr, + "Generate SPIR-V output directly (otherwise through " + "GLSL and using the glslang compiler)"}, + { OptionKind::FileSystem, "-file-system", "-file-system <fs>", + "Set the filesystem hook to use for a compile request.\n" + "Accepted file systems: default, load-file, os" }, + { OptionKind::Heterogeneous, "-heterogeneous", nullptr, "Output heterogeneity-related code." }, + { OptionKind::NoMangle, "-no-mangle", nullptr, "Do as little mangling of names as possible." } + }; + _addOptions(makeConstArrayView(experimentalOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Internal !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Internal"); + + const Option internalOpts[] = + { + { OptionKind::ArchiveType, "-archive-type", "-archive-type <archive-type>", "Set the archive type for -save-stdlib. Default is zip." }, + { OptionKind::CompileStdLib, "-compile-stdlib", nullptr, + "Compile the StdLib from embedded sources. " + "Will return a failure if there is already a StdLib available."}, + { OptionKind::Doc, "-doc", nullptr, "Write documentation for -compile-stdlib" }, + { OptionKind::IrCompression,"-ir-compression", "-ir-compression <type>", + "Set compression for IR and AST outputs.\n" + "Accepted compression types: none, lite"}, + { OptionKind::LoadStdLib, "-load-stdlib", "-load-stdlib <filename>", "Load the StdLib from file." }, + { OptionKind::ReferenceModule, "-r", "-r <name>", "reference module <name>" }, + { OptionKind::SaveStdLib, "-save-stdlib", "-save-stdlib <filename>", "Save the StdLib modules to an archive file." }, + { OptionKind::SaveStdLibBinSource, "-save-stdlib-bin-source","-save-stdlib-bin-source <filename>", "Same as -save-stdlib but output " + "the data as a C array.\n"}, + { OptionKind::TrackLiveness, "-track-liveness", nullptr, "Enable liveness tracking. Places SLANG_LIVE_START, and SLANG_LIVE_END in output source to indicate value liveness." }, + }; + _addOptions(makeConstArrayView(internalOpts), options); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Depreciated !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + options.setCategory("Depreciated"); + + const Option depreciatedOpts[] = + { + { OptionKind::ParameterBlocksUseRegisterSpaces, "-parameter-blocks-use-register-spaces", nullptr, "Parameter blocks will use register spaces" }, + }; + _addOptions(makeConstArrayView(depreciatedOpts), options); + + // We can now check that the whole range is available. If this fails it means there + // is an enum in the list that hasn't been setup as an option! + SLANG_ASSERT(options.hasContiguousUserValueRange(CommandOptions::LookupKind::Option, UserValue(0), UserValue(OptionKind::CountOf))); + SLANG_ASSERT(options.hasContiguousUserValueRange(CommandOptions::LookupKind::Category, UserValue(0), UserValue(ValueCategory::CountOf))); +} + SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifact); struct OptionsParser @@ -516,231 +998,6 @@ struct OptionsParser return SLANG_OK; } - static char const* getHelpText() - { -#ifdef _WIN32 -#define EXECUTABLE_EXTENSION ".exe" -#else -#define EXECUTABLE_EXTENSION "" -#endif - - return - "Usage: slangc" EXECUTABLE_EXTENSION " [options...] [--] <input files>\n" - "\n" - "General options:\n" - "\n" - " -D<name>[=<value>], -D <name>[=<value>]: Insert a preprocessor macro.\n" - " -depfile <path>: Save the source file dependency list in a file.\n" - " -entry <name>: Specify the name of an entry-point function.\n" - " Multiple -entry options may be used in a single invocation.\n" - " If no -entry options are given, compiler will use [shader(...)]\n" - " attributes to detect entry points.\n" - " -h, -help, --help: Print this message.\n" - " -I<path>, -I <path>: Add a path to be used in resolving '#include'\n" - " and 'import' operations.\n" - " -lang <language>: Set the language for the following input files.\n" - " Accepted languages are:\n" - " c, cpp, c++, cxx, slang, glsl, hlsl, cu, cuda\n" - " -matrix-layout-column-major: Set the default matrix layout to column-major.\n" - " -matrix-layout-row-major: Set the default matrix layout to row-major.\n" - " -module-name <name>: Set the module name to use when compiling multiple\n" - " .slang source files into a single module.\n" - " -o <path>: Specify a path where generated output should be written.\n" - " If no -target or -stage is specified, one may be inferred\n" - " from file extension (see File Extensions).\n" - " If multiple -target options and a single -entry are present, each -o\n" - " associates with the first -target to its left.\n" - " Otherwise, if multiple -entry options are present, each -o associates\n" - " with the first -entry to its left, and with the -target that matches\n" - " the one inferred from <path>.\n" - " -profile <profile>[+<capability>...]: Specify the shader profile for code\n" - " generation.\n" - " Accepted profiles are:\n" - " sm_{4_0,4_1,5_0,5_1,6_0,6_1,6_2,6_3,6_4,6_5,6_6}\n" - " glsl_{110,120,130,140,150,330,400,410,420,430,440,450,460}\n" - " Additional profiles that include -stage information:\n" - " {vs,hs,ds,gs,ps}_<version>\n" - " See -capability for information on <capability>\n" - " When multiple -target options are present, each -profile associates\n" - " with the first -target to its left.\n" - " -stage <name>: Specify the stage of an entry-point function.\n" - " Accepted stages are:\n" - " vertex, hull, domain, geometry, fragment, compute,\n" - " raygeneration, intersection, anyhit, closesthit, miss, callable\n" - " When multiple -entry options are present, each -stage associated with\n" - " the first -entry to its left.\n" - " May be omitted if entry-point function has a [shader(...)] attribute;\n" - " otherwise required for each -entry option.\n" - " -target <format>: Specifies the format in which code should be generated.\n" - " Accepted formats are:\n" - " glsl, hlsl, spirv, spirv-assembly, dxbc,\n" - " dxbc-assembly, dxil, dxil-assembly\n" - " -v, -version: Display the build version.\n" - " -warnings-as-errors all: Treat all warnings as errors.\n" - " -warnings-as-errors <id>[,<id>...]: Treat specific warning ids as errors.\n" - " -warnings-disable <id>[,<id>...]: Disable specific warning ids.\n" - " -W<id>: Enable a warning with the specified id.\n" - " -Wno-<id>: Disable a warning with the specified id.\n" - " -dump-warning-diagnostics: Dump to output list of warning diagnostic numeric and name ids.\n" - " --: Treat the rest of the command line as input files.\n" - "\n" - "Target code generation options:\n" - "\n" - " -capability <capability>[+<capability>...]: Add optional capabilities\n" - " to a code generation target. See Capabilities below.\n" - " -default-image-format-unknown: Set the format of R/W images with unspecified\n" - " format to 'unknown'. Otherwise try to guess the format.\n" - " -disable-dynamic-dispatch: Disables generating dynamic dispatch code.\n" - " -disable-specialization: Disables generics and specialization pass.\n" - " -fp-mode <mode>, -floating-point-mode <mode>: Set the floating point mode.\n" - " Accepted modes are:\n" - " precise : Disable optimization that could change the output of floating-\n" - " point computations, including around infinities, NaNs, denormalized\n" - " values, and negative zero. Prefer the most precise versions of special\n" - " functions supported by the target.\n" - " fast : Allow optimizations that may change results of floating-point\n" - " computations. Prefer the fastest version of special functions supported\n" - " by the target.\n" - " -g, -g<N>: Include debug information in the generated code, where possible.\n" - " N is the amount of information, 0..3, unspecified means 2\n" - " -line-directive-mode <mode>: Sets how the `#line` directives should be\n" - " produced. Available options are:\n" - " none : Don't emit `#line` directives at all\n" - " source-map : Use source map to track line associations (doen't emit #line)\n" - " default : Default behavior\n" - " If not specified, default behavior is to use C-style `#line` directives\n" - " for HLSL and C/C++ output, and traditional GLSL-style `#line` directives\n" - " for GLSL output.\n" - " -O<N>: Set the optimization level.\n" - " N is the amount of optimization, 0..3, default is 1\n" - " -obfuscate: Remove all source file information from outputs.\n" - "\n" - "Downstream compiler options:\n" - "\n" - " -<compiler>-path: Specify path to a downstream <compiler>\n" - " executable or library. Accepted compilers are:\n" - " fxc (d3dcompiler_47.dll)\n" - " dxc (dxcompiler.*)\n" - " glslang (slang-glslang.*)\n" - " vs = visualstudio (cl.exe)\n" - " clang\n" - " gcc (g++)\n" - " c = cpp = genericcpp\n" - " nvrtc\n" - " llvm\n" - " -default-downstream-compiler <language> <compiler>: Set a default compiler\n" - " for the given language. See -lang for the list of languages.\n" - " -X<compiler> <option>: Pass arguments to downstream <compiler>.\n" - "\n" - "Compiler debugging/instrumentation options:\n" - "\n" - " -dump-ast: Dump the AST to a .slang-ast file next to the input.\n" - " -dump-intermediate-prefix <prefix>: File name prefix for -dump-intermediates \n" - " outputs, default is 'slang-dump-'\n" - " -dump-intermediates: Dump intermediate outputs for debugging.\n" - " -dump-ir: Dump the IR for debugging.\n" - " -dump-ir-ids: Dump the IDs with -dump-ir (debug builds only)\n" - " -dump-repro: Dump a `.slang-repro` file that can be used to reproduce\n" - " a compilation on another machine.\n" - " -dump-repro-on-error: Dump `.slang-repro` file on any compilation error.\n" - " -E, -output-preprocessor: Output the preprocessing result and exit.\n" - " -extract-repro <name>: Extract the repro files into a folder.\n" - " -load-repro <name>\n" - " -load-repro-directory <path>\n" - " -no-codegen: Skip the code generation step, just check the code and\n" - " generate layout.\n" - " -output-includes: Print the hierarchy of the processed source files.\n" - " -pass-through <name>: Pass the input through mostly unmodified to the \n" - " existing compiler <name>. Accepted compilers are:\n" - " fxc, glslang, dxc\n" - " -repro-file-system <name>\n" - " -serial-ir: Serialize the IR between front-end and back-end.\n" - " -skip-codegen: Skip the code generation phase.\n" - " -validate-ir: Validate the IR between the phases.\n" - " -verbose-paths: Display more detailed paths in diagnostic output.\n" - " -verify-debug-serial-ir: Verify IR in the front-end.\n" - "\n" - "Experimental options (use at your own risk):\n" - "\n" - " -emit-spirv-directly: Generate SPIR-V output directly (otherwise through \n" - " GLSL and using the glslang compiler)\n" - " -file-system <fs>: Set the filesystem hook to use for a compile request.\n" - " Accepted file systems:\n" - " default, load-file, os\n" - " -heterogeneous: Output heterogeneity-related code.\n" - " -no-mangle: Do as little mangling of names as possible.\n" - "\n" - "Internal-use options (use at your own risk):\n" - "\n" - " -archive-type <type>: Set the archive type for -save-stdlib. Default is zip.\n" - " Accepted archive types:\n" - " zip, riff, riff-deflate, riff-lz4\n" - " -compile-stdlib: Compile the StdLib from embedded sources.\n" - " Will return a failure if there is already a StdLib available.\n" - " -doc: Write documentation for -compile-stdlib\n" - " -ir-compression <type>: Set compression for IR and AST outputs.\n" - " Accepted compression types:\n" - " none, lite\n" - " -load-stdlib <filename>: Load the StdLib from file.\n" - " -r <name>: reference module <name>\n" - " -save-stdlib <filename>: Save the StdLib modules to an archive file.\n" - " -save-stdlib-bin-source <filename>: Same as -save-stdlib but output\n" - " the data as a C array.\n" - " -track-liveness: Enable liveness tracking. Places SLANG_LIVE_START, and SLANG_LIVE_END in output source to indicate value liveness.\n" - " -source-map: Enables outputting of a source map. Note this is *distinct* from line-directive-mode.\n" - "\n" - "Deprecated options (allowed but ignored; may be removed in future):\n" - "\n" - " -parameter-blocks-use-register-spaces\n" - "\n" - "File Extensions:\n" - "\n" - " A <language>, <format>, and/or <stage> may be inferred from the\n" - " extension of an input or -o path:\n" - "\n" - " extension | language/format | stage\n" - " ====================|=================|======\n" - " .hlsl, .fx -> hlsl\n" - " .dxbc -> dxbc\n" - " .dxbc-asm -> dxbc-assembly\n" - " .dxil -> dxil\n" - " .dxil-asm -> dxil-assembly\n" - " .glsl -> glsl\n" - " .vert -> glsl vertex\n" - " .frag -> glsl fragment\n" - " .geom -> glsl geoemtry\n" - " .tesc -> glsl hull\n" - " .tese -> glsl domain\n" - " .comp -> glsl compute\n" - " .slang -> slang\n" - " .spv -> spirv\n" - " .spv-asm -> spirv-assembly\n" - " .c -> c\n" - " .cpp, .c++, .cxx -> c++\n" - " .exe -> executable\n" - " .dll, .so -> sharedlibrary\n" - " .cu -> cuda\n" - " .ptx -> ptx\n" - " .obj, .o -> object-code\n" - "\n" - "Capabilities:\n" - "\n" - " A capability describes an optional feature that a target may or\n" - " may not support. When a -capability is specified, the compiler\n" - " may assume that the target supports that capability, and generate\n" - " code accordingly.\n" - " Currently defined capabilities are:\n" - "\n" - " spirv_1_{0,1,2,3,4,5} - minimum supported SPIR-V version\n" - " GL_NV_ray_tracing - enables the GL_NV_ray_tracing extension\n" - " GL_EXT_ray_tracing - enables the GL_EXT_ray_tracing extension\n" - " GL_NV_fragment_shader_barycentric - enables the GL_NV_fragment_shader_barycentric extension\n" - " GL_EXT_fragment_shader_barycentric - enables the GL_EXT_fragment_shader_barycentric extension\n" - "\n"; - -#undef EXECUTABLE_EXTENSION - } - // Pass Severity::Disabled to allow any original severity SlangResult _overrideDiagnostics(const UnownedStringSlice& identifierList, Severity originalSeverity, Severity overrideSeverity, DiagnosticSink* sink) { @@ -830,6 +1087,59 @@ struct OptionsParser return SLANG_OK; } + SlangResult _getValue(ValueCategory valueCategory, const CommandLineArg& arg, DiagnosticSink* sink, CommandOptions::UserValue& outValue) + { + auto& cmdOptions = asInternal(session)->m_commandOptions; + + const auto optionIndex = cmdOptions.findOptionByCategoryUserValue(CommandOptions::UserValue(valueCategory), arg.value.getUnownedSlice()); + if (optionIndex < 0) + { + const auto categoryIndex = cmdOptions.findCategoryByUserValue(CommandOptions::UserValue(valueCategory)); + SLANG_ASSERT(categoryIndex >= 0); + if (categoryIndex < 0) + { + return SLANG_FAIL; + } + + List<UnownedStringSlice> names; + cmdOptions.getCategoryOptionNames(categoryIndex, names); + + StringBuilder buf; + StringUtil::join(names.getBuffer(), names.getCount(), toSlice(", "), buf); + + sink->diagnose(arg.loc, Diagnostics::unknownCommandLineValue, buf); + return SLANG_FAIL; + } + + outValue = cmdOptions.getOptionAt(optionIndex).userValue; + return SLANG_OK; + } + SlangResult _expectValue(ValueCategory valueCategory, CommandLineReader& reader, DiagnosticSink* sink, CommandOptions::UserValue& outValue) + { + CommandLineArg arg; + SLANG_RETURN_ON_FAIL(reader.expectArg(arg)); + SLANG_RETURN_ON_FAIL(_getValue(valueCategory, arg, sink, outValue)); + return SLANG_OK; + } + + void _appendUsageTitle(StringBuilder& out) + { + out << "Usage: slangc [options...] [--] <input files>\n\n"; + } + void _appendMinimalUsage(StringBuilder& out) + { + _appendUsageTitle(out); + out << "For help: slangc -h\n"; + } + void _outputMinimalUsage(DiagnosticSink* sink) + { + // Output usage info + StringBuilder buf; + _appendMinimalUsage(buf); + + sink->diagnoseRaw(Severity::Note, buf.getUnownedSlice()); + } + SlangResult parse( int argc, char const* const* argv) @@ -896,23 +1206,39 @@ struct OptionsParser slang::CompileStdLibFlags compileStdLibFlags = 0; bool hasLoadedRepro = false; + // Get the options on the session + CommandOptions& options = asInternal(session)->m_commandOptions; + + auto frontEndReq = requestImpl->getFrontEndReq(); + while (reader.hasArg()) { auto arg = reader.getArgAndAdvance(); const auto& argValue = arg.value; - if (argValue[0] == '-') + // If it's not an option we assume it's a path + if (argValue[0] != '-') { - if(argValue == "-no-mangle" ) - { - flags |= SLANG_COMPILE_FLAG_NO_MANGLING; - } - else if (argValue == toSlice("-emit-ir")) - { - // Enable emitting IR - requestImpl->m_emitIr = true; - } - else if (argValue == "-load-stdlib") + SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer())); + continue; + } + + const Index optionIndex = options.findOptionByName(argValue.getUnownedSlice()); + + if (optionIndex < 0) + { + sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue); + _outputMinimalUsage(sink); + return SLANG_FAIL; + } + + const auto optionKind = OptionKind(options.getOptionAt(optionIndex).userValue); + + 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)); @@ -921,24 +1247,17 @@ struct OptionsParser ScopedAllocation contents; SLANG_RETURN_ON_FAIL(File::readAllBytes(fileName.value, contents)); SLANG_RETURN_ON_FAIL(session->loadStdLib(contents.getData(), contents.getSizeInBytes())); + break; } - else if (argValue == "-compile-stdlib") - { - compileStdLib = true; - } - else if (argValue == "-archive-type") + case OptionKind::CompileStdLib: compileStdLib = true; break; + case OptionKind::ArchiveType: { - CommandLineArg archiveTypeName; - SLANG_RETURN_ON_FAIL(reader.expectArg(archiveTypeName)); - - archiveType = TypeTextUtil::findArchiveType(archiveTypeName.value.getUnownedSlice()); - if (archiveType == SLANG_ARCHIVE_TYPE_UNDEFINED) - { - sink->diagnose(archiveTypeName.loc, Diagnostics::unknownArchiveType, archiveTypeName.value); - return SLANG_FAIL; - } + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::ArchiveType, reader, sink, value)); + archiveType = SlangArchiveType(value); + break; } - else if (argValue == "-save-stdlib") + case OptionKind::SaveStdLib: { CommandLineArg fileName; SLANG_RETURN_ON_FAIL(reader.expectArg(fileName)); @@ -947,8 +1266,9 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(session->saveStdLib(archiveType, blob.writeRef())); SLANG_RETURN_ON_FAIL(File::writeAllBytes(fileName.value, blob->getBufferPointer(), blob->getBufferSize())); + break; } - else if (argValue == "-save-stdlib-bin-source") + case OptionKind::SaveStdLibBinSource: { CommandLineArg fileName; SLANG_RETURN_ON_FAIL(reader.expectArg(fileName)); @@ -963,61 +1283,45 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(HexDumpUtil::dumpSourceBytes((const uint8_t*)blob->getBufferPointer(), blob->getBufferSize(), 16, &writer)); File::writeAllText(fileName.value, builder); + break; } - else if (argValue == "-no-codegen") - { - flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; - } - else if (argValue == "-dump-intermediates") - { - compileRequest->setDumpIntermediates(true); - } - else if (argValue == "-dump-ir-ids") + case OptionKind::NoCodeGen: flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; break; + case OptionKind::DumpIntermediates: compileRequest->setDumpIntermediates(true); break; + case OptionKind::DumpIrIds: { - requestImpl->getFrontEndReq()->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds; + frontEndReq->m_irDumpOptions.flags |= IRDumpOptions::Flag::DumpDebugIds; + break; } - else if (argValue == "-dump-intermediate-prefix") + case OptionKind::DumpIntermediatePrefix: { CommandLineArg prefix; SLANG_RETURN_ON_FAIL(reader.expectArg(prefix)); requestImpl->m_dumpIntermediatePrefix = prefix.value; + break; } - else if (argValue == "-output-includes") - { - requestImpl->getFrontEndReq()->outputIncludes = true; - } - else if(argValue == "-dump-ir" ) - { - requestImpl->getFrontEndReq()->shouldDumpIR = true; - } - else if (argValue == "-E" || argValue == "-output-preprocessor") - { - requestImpl->getFrontEndReq()->outputPreprocessor = true; - } - else if (argValue == "-dump-ast") - { - requestImpl->getFrontEndReq()->shouldDumpAST = true; - } - else if (argValue == "-doc") + 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; // Enable writing out documentation on the req - requestImpl->getFrontEndReq()->shouldDocument = true; + frontEndReq->shouldDocument = true; + break; } - else if (argValue == "-dump-repro") + case OptionKind::DumpRepro: { CommandLineArg dumpRepro; SLANG_RETURN_ON_FAIL(reader.expectArg(dumpRepro)); requestImpl->m_dumpRepro = dumpRepro.value; compileRequest->enableReproCapture(); + break; } - else if (argValue == "-dump-repro-on-error") - { - requestImpl->m_dumpReproOnError = true; - } - else if (argValue == "-extract-repro") + case OptionKind::DumpReproOnError: requestImpl->m_dumpReproOnError = true; break; + case OptionKind::ExtractRepro: { CommandLineArg reproName; SLANG_RETURN_ON_FAIL(reader.expectArg(reproName)); @@ -1030,15 +1334,17 @@ struct OptionsParser return res; } } + break; } - else if (argValue == "-module-name") + case OptionKind::ModuleName: { CommandLineArg moduleName; SLANG_RETURN_ON_FAIL(reader.expectArg(moduleName)); compileRequest->setDefaultModuleName(moduleName.value.getBuffer()); + break; } - else if(argValue == "-load-repro") + case OptionKind::LoadRepro: { CommandLineArg reproName; SLANG_RETURN_ON_FAIL(reader.expectArg(reproName)); @@ -1072,15 +1378,17 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, requestImpl)); hasLoadedRepro = true; + break; } - else if (argValue == "-load-repro-directory") + case OptionKind::LoadReproDirectory: { CommandLineArg reproDirectory; SLANG_RETURN_ON_FAIL(reader.expectArg(reproDirectory)); SLANG_RETURN_ON_FAIL(_compileReproDirectory(session, requestImpl, reproDirectory.value, sink)); + break; } - else if (argValue == "-repro-file-system") + case OptionKind::ReproFileSystem: { CommandLineArg reproName; SLANG_RETURN_ON_FAIL(reader.expectArg(reproName)); @@ -1122,32 +1430,15 @@ struct OptionsParser // Set as the file system compileRequest->setFileSystem(fileSystem); + break; } - else if (argValue == "-serial-ir") - { - requestImpl->getFrontEndReq()->useSerialIRBottleneck = true; - } - else if (argValue == "-disable-specialization") - { - requestImpl->disableSpecialization = true; - } - else if (argValue == "-disable-dynamic-dispatch") - { - requestImpl->disableDynamicDispatch = true; - } - else if (argValue == "-track-liveness") - { - requestImpl->setTrackLiveness(true); - } - else if (argValue == "-verbose-paths") - { - requestImpl->getSink()->setFlag(DiagnosticSink::Flag::VerbosePath); - } - else if (argValue == "-dump-warning-diagnostics") - { - _dumpDiagnostics(Severity::Warning, sink); - } - else if (argValue == "-warnings-as-errors") + 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)); @@ -1162,52 +1453,47 @@ struct OptionsParser { SLANG_RETURN_ON_FAIL(_overrideDiagnostics(operand.value.getUnownedSlice(), Severity::Warning, Severity::Error, sink)); } + break; } - else if (argValue == "-warnings-disable") + 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; } - else if (argValue.startsWith(toSlice("-W"))) - { - auto name = argValue.getUnownedSlice().tail(2); - - // If prefixed with 'no-', disable the warning - if (name.startsWith(toSlice("no-"))) - { - SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name.tail(3), Severity::Warning, Severity::Disable, sink)); - } - else - { - // Enable the warning - SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning, sink)); - } - } - else if (argValue == "-verify-debug-serial-ir") - { - requestImpl->getFrontEndReq()->verifyDebugSerialization = true; - } - else if(argValue == "-validate-ir" ) + case OptionKind::DisableWarning: { - requestImpl->getFrontEndReq()->shouldValidateIR = true; + // 5 because -Wno- + auto name = argValue.getUnownedSlice().tail(5); + SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Disable, sink)); + break; } - else if(argValue == "-skip-codegen" ) + case OptionKind::EnableWarning: { - requestImpl->m_shouldSkipCodegen = true; + // 2 because -W + auto name = argValue.getUnownedSlice().tail(5); + // Enable the warning + SLANG_RETURN_ON_FAIL(_overrideDiagnostic(name, Severity::Warning, Severity::Warning, sink)); + break; } - else if(argValue == "-parameter-blocks-use-register-spaces" ) + 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; + getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES; + break; } - else if (argValue == "-ir-compression") + case OptionKind::IrCompression: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); SLANG_RETURN_ON_FAIL(SerialParseUtil::parseCompressionType(name.value.getUnownedSlice(), requestImpl->getLinkage()->serialCompressionType)); + break; } - else if (argValue == "-target") + case OptionKind::Target: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1224,12 +1510,14 @@ struct OptionsParser rawTarget.format = CodeGenTarget(format); rawTargets.add(rawTarget); + break; } - // 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. - else if (argValue == "-profile") + 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)); @@ -1284,8 +1572,10 @@ struct OptionsParser addCapabilityAtom(getCurrentTarget(), atom); } + + break; } - else if( argValue == "-capability" ) + case OptionKind::Capability: { // The `-capability` option is similar to `-profile` but does not set the actual profile // for a target (it just adds capabilities). @@ -1313,8 +1603,9 @@ struct OptionsParser addCapabilityAtom(getCurrentTarget(), atom); } + break; } - else if (argValue == "-stage") + case OptionKind::Stage: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1329,8 +1620,9 @@ struct OptionsParser { setStage(getCurrentEntryPoint(), stage); } + break; } - else if (argValue == "-entry") + case OptionKind::EntryPointName: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1340,8 +1632,9 @@ struct OptionsParser rawEntryPoint.translationUnitIndex = currentTranslationUnitIndex; rawEntryPoints.add(rawEntryPoint); + break; } - else if (argValue == "-lang") + case OptionKind::Language: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1360,8 +1653,9 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer(), sourceLanguage)); } } + break; } - else if (argValue == "-pass-through") + case OptionKind::PassThrough: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1374,8 +1668,9 @@ struct OptionsParser } compileRequest->setPassThrough(passThrough); + break; } - else if (argValue.getLength() >= 2 && argValue[1] == 'D') + case OptionKind::MacroDefine: { // The value to be defined might be part of the same option, as in: // -DFOO @@ -1396,7 +1691,7 @@ struct OptionsParser const Index equalIndex = slice.indexOf('='); // Now set the preprocessor define - + if (equalIndex >= 0) { // If we found an `=`, we split the string... @@ -1407,8 +1702,9 @@ struct OptionsParser // If there was no `=`, then just #define it to an empty string compileRequest->addPreprocessorDefine(String(slice).getBuffer(), ""); } + break; } - else if (argValue.getLength() >= 2 && argValue[1] == 'I') + case OptionKind::Include: { // The value to be defined might be part of the same option, as in: // -IFOO @@ -1426,18 +1722,19 @@ struct OptionsParser } compileRequest->addSearchPath(String(slice).getBuffer()); + break; } - // - // A `-o` option is used to specify a desired output file. - else if (argValue == "-o") + case OptionKind::Output: { + // + // 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; } - // A -depfile option is used to specify the file name where the dependency lists will be written - else if (argValue == "-depfile") + case OptionKind::DepFile: { CommandLineArg dependencyPath; SLANG_RETURN_ON_FAIL(reader.expectArg(dependencyPath)); @@ -1451,65 +1748,25 @@ struct OptionsParser sink->diagnose(dependencyPath.loc, Diagnostics::duplicateDependencyOutputPaths); return SLANG_FAIL; } + break; } - else if(argValue == "-matrix-layout-row-major") - { - defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor); - } - else if(argValue == "-matrix-layout-column-major") - { - defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor); - } - else if(argValue == "-line-directive-mode") + case OptionKind::MatrixLayoutRow: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_RowMajor); break; + case OptionKind::MatrixLayoutColumn: defaultMatrixLayoutMode = SlangMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor); break; + case OptionKind::LineDirectiveMode: { - CommandLineArg name; - SLANG_RETURN_ON_FAIL(reader.expectArg(name)); - - SlangLineDirectiveMode mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT; - - if(name.value == toSlice("none")) - { - mode = SLANG_LINE_DIRECTIVE_MODE_NONE; - } - else if (name.value == toSlice("source-map")) - { - mode = SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP; - } - else if (name.value == toSlice("default")) - { - mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT; - } - else - { - sink->diagnose(name.loc, Diagnostics::unknownLineDirectiveMode, name.value); - return SLANG_FAIL; - } - - compileRequest->setLineDirectiveMode(mode); + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::LineDirectiveMode, reader, sink, value)); + compileRequest->setLineDirectiveMode(SlangLineDirectiveMode(value)); + break; } - else if( argValue == "-fp-mode" || argValue == "-floating-point-mode" ) + case OptionKind::FloatingPointMode: { - CommandLineArg name; - SLANG_RETURN_ON_FAIL(reader.expectArg(name)); - - FloatingPointMode mode = FloatingPointMode::Default; - if(name.value == "fast") - { - mode = FloatingPointMode::Fast; - } - else if(name.value == "precise") - { - mode = FloatingPointMode::Precise; - } - else - { - sink->diagnose(name.loc, Diagnostics::unknownFloatingPointMode, name.value); - return SLANG_FAIL; - } - - setFloatingPointMode(getCurrentTarget(), mode); + CommandOptions::UserValue value; + SLANG_RETURN_ON_FAIL(_expectValue(ValueCategory::FloatingPointMode, reader, sink, value)); + setFloatingPointMode(getCurrentTarget(), FloatingPointMode(value)); + break; } - else if( argValue.getLength() >= 2 && argValue[1] == 'O' ) + case OptionKind::Optimization: { UnownedStringSlice levelSlice = argValue.getUnownedSlice().tail(2); SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; @@ -1530,12 +1787,13 @@ struct OptionsParser } compileRequest->setOptimizationLevel(level); + break; } - // 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. - else if (argValue.startsWith("-g")) + case OptionKind::DebugInformation: { + // 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 (argValue == toSlice("-g")) { // The default is standard @@ -1594,16 +1852,11 @@ struct OptionsParser compileRequest->setDebugInfoFormat(format); } + break; } - else if( argValue == "-default-image-format-unknown" ) - { - requestImpl->useUnknownImageFormatAsDefault = true; - } - else if (argValue == "-obfuscate") - { - requestImpl->getLinkage()->m_obfuscateCode = true; - } - else if (argValue == "-file-system") + case OptionKind::DefaultImageFormatUnknown: requestImpl->useUnknownImageFormatAsDefault = true; break; + case OptionKind::Obfuscate: requestImpl->getLinkage()->m_obfuscateCode = true; break; + case OptionKind::FileSystem: { CommandLineArg name; SLANG_RETURN_ON_FAIL(reader.expectArg(name)); @@ -1627,8 +1880,9 @@ struct OptionsParser sink->diagnose(name.loc, Diagnostics::unknownFileSystemOption, name.value); return SLANG_FAIL; } + break; } - else if (argValue == "-r") + case OptionKind::ReferenceModule: { CommandLineArg referenceModuleName; SLANG_RETURN_ON_FAIL(reader.expectArg(referenceModuleName)); @@ -1689,21 +1943,50 @@ struct OptionsParser artifact->addRepresentation(fileRep); SLANG_RETURN_ON_FAIL(_addLibraryReference(requestImpl, artifact)); + break; } - else if (argValue == "-v" || argValue == "-version") + case OptionKind::Version: { sink->diagnoseRaw(Severity::Note, session->getBuildTagString()); + break; } - else if (argValue == "-h" || argValue == "-help" || argValue == "--help") + case OptionKind::Help: { - sink->diagnoseRaw(Severity::Note, getHelpText()); + + Index categoryIndex = -1; + + if (reader.hasArg()) + { + auto catArg = reader.getArgAndAdvance(); + + categoryIndex = options.findCategoryByCaseInsensitiveName(catArg.value.getUnownedSlice()); + if (categoryIndex) + { + sink->diagnose(catArg.loc, Diagnostics::unknownHelpCategory); + return SLANG_FAIL; + } + } + + CommandOptionsWriter writer; + auto& buf = writer.getBuilder(); + + if (categoryIndex < 0) + { + _appendUsageTitle(buf); + writer.appendDescription(options); + } + else + { + writer.appendDescriptionForCategory(options, categoryIndex); + } + + sink->diagnoseRaw(Severity::Note, buf.getBuffer()); + return SLANG_FAIL; } - else if( argValue == "-emit-spirv-directly" ) - { - getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; - } - else if (argValue == "-default-downstream-compiler") + case OptionKind::EmitSpirvDirectly: getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; break; + + case OptionKind::DefaultDownstreamCompiler: { CommandLineArg sourceLanguageArg, compilerArg; SLANG_RETURN_ON_FAIL(reader.expectArg(sourceLanguageArg)); @@ -1728,8 +2011,34 @@ struct OptionsParser sink->diagnose(arg.loc, Diagnostics::unableToSetDefaultDownstreamCompiler, compilerArg.value, sourceLanguageArg.value); return SLANG_FAIL; } + break; } - else if (argValue == "--") + 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: @@ -1738,43 +2047,16 @@ struct OptionsParser SLANG_RETURN_ON_FAIL(addInputPath(reader.getValueAndAdvance().getBuffer())); } break; - } - else + } + default: { - if (argValue.endsWith("-path")) - { - 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; - } - } - } - + // Hmmm, we looked up and produced a valid enum, but it wasn't handled in the switch... sink->diagnose(arg.loc, Diagnostics::unknownCommandLineOption, argValue); - // TODO: print a usage message + + _outputMinimalUsage(sink); return SLANG_FAIL; } } - else - { - SLANG_RETURN_ON_FAIL(addInputPath(argValue.getBuffer())); - } } if (compileStdLib) @@ -2371,7 +2653,6 @@ struct OptionsParser } }; - SlangResult parseOptions( SlangCompileRequest* inCompileRequest, int argc, diff --git a/source/slang/slang-options.h b/source/slang/slang-options.h index 2426c08cb..bb05312a3 100644 --- a/source/slang/slang-options.h +++ b/source/slang/slang-options.h @@ -7,6 +7,7 @@ namespace Slang { +struct CommandOptions; UnownedStringSlice getCodeGenTargetName(SlangCompileTarget target); @@ -15,5 +16,8 @@ SlangResult parseOptions( int argc, char const* const* argv); +// Initialize command options. Holds the details how parsing works. +void initCommandOptions(CommandOptions& commandOptions); + } #endif diff --git a/source/slang/slang-profile.h b/source/slang/slang-profile.h index 661274ad4..0adb9dee9 100644 --- a/source/slang/slang-profile.h +++ b/source/slang/slang-profile.h @@ -50,6 +50,7 @@ namespace Slang #define PROFILE_STAGE(TAG, NAME, VAL) TAG = VAL, #define PROFILE_STAGE_ALIAS(TAG, NAME, VAL) TAG = VAL, #include "slang-profile-defs.h" + }; const char* getStageName(Stage stage); @@ -110,7 +111,14 @@ namespace Slang RawVal raw = Unknown; }; + struct StageInfo + { + const char* name; + Stage stage; + }; + /// Note! There can be multiple stages with the same name + ConstArrayView<StageInfo> getStageInfos(); Stage findStageByName(String const& name); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 8aea1bc37..9c75aa63e 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -132,6 +132,7 @@ void Session::init() { SLANG_ASSERT(BaseTypeInfo::check()); + _initCodeGenTransitionMap(); ::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators)); @@ -144,6 +145,9 @@ void Session::init() m_sharedLibraryLoader = DefaultSharedLibraryLoader::getSingleton(); + // Set up the command line options + initCommandOptions(m_commandOptions); + // Set up shared AST builder m_sharedASTBuilder = new SharedASTBuilder; m_sharedASTBuilder->init(this); |
