diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-05-31 17:20:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-31 17:20:37 -0400 |
| commit | 6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch) | |
| tree | 5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/options.cpp | |
| parent | b81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff) | |
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang-
* Prefix source in source/slang with slang- prefix.
* Rename core source files with slang- prefix.
* Update project files.
* Fix problems from automatic merge.
Diffstat (limited to 'source/slang/options.cpp')
| -rw-r--r-- | source/slang/options.cpp | 1356 |
1 files changed, 0 insertions, 1356 deletions
diff --git a/source/slang/options.cpp b/source/slang/options.cpp deleted file mode 100644 index 46e0203cf..000000000 --- a/source/slang/options.cpp +++ /dev/null @@ -1,1356 +0,0 @@ -// options.cpp - -// Implementation of options parsing for `slangc` command line, -// and also for API interface that takes command-line argument strings. - -#include "../../slang.h" - -#include "compiler.h" -#include "profile.h" - -#include <assert.h> - -namespace Slang { - -SlangResult tryReadCommandLineArgumentRaw(DiagnosticSink* sink, char const* option, char const* const**ioCursor, char const* const*end, char const** argOut) -{ - *argOut = nullptr; - char const* const*& cursor = *ioCursor; - if (cursor == end) - { - sink->diagnose(SourceLoc(), Diagnostics::expectedArgumentForOption, option); - return SLANG_FAIL; - } - else - { - *argOut = *cursor++; - return SLANG_OK; - } -} - -SlangResult tryReadCommandLineArgument(DiagnosticSink* sink, char const* option, char const* const**ioCursor, char const* const*end, String& argOut) -{ - const char* arg; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgumentRaw(sink, option, ioCursor, end, &arg)); - argOut = arg; - return SLANG_OK; -} - -struct OptionsParser -{ - SlangSession* session = nullptr; - SlangCompileRequest* compileRequest = nullptr; - - Slang::EndToEndCompileRequest* requestImpl = nullptr; - - Slang::RefPtr<Slang::ConfigurableSharedLibraryLoader> sharedLibraryLoader; - - // A "translation unit" represents one or more source files - // that are processed as a single entity when it comes to - // semantic checking. - // - // For languages like HLSL, GLSL, and C, a translation unit - // is usually a single source file (which can then go on - // to `#include` other files into the same translation unit). - // - // For Slang, we support having multiple source files in - // a single translation unit, and indeed command-line `slangc` - // will always put all the source files into a single translation - // unit. - // - // We track information on the translation units that we - // create during options parsing, so that we can assocaite - // other entities with these translation units: - // - struct RawTranslationUnit - { - // What language is the translation unit using? - // - // Note: We do not support translation units that mix - // languages. - // - SlangSourceLanguage sourceLanguage; - - // Certain naming conventions imply a stage for - // a file with only a single entry point, and in - // those cases we will try to infer the stage from - // the file when it is possible. - // - Stage impliedStage; - - // We retain the Slang API level translation unit index, - // which we will call an "ID" inside the options parsing code. - // - // This will almost always be the index into the - // `rawTranslationUnits` array below, but could conceivably, - // be mismatched if we were parsing options for a compile - // request that already had some translation unit(s) added - // manually. - // - 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 - // `-stage` command line option, or a `[shader("...")]` attribute - // in the source code. - // - struct RawEntryPoint - { - String name; - Stage stage = Stage::Unknown; - int translationUnitIndex = -1; - int entryPointID = -1; - - // State for tracking command-line errors - 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; - CodeGenTarget impliedFormat = CodeGenTarget::Unknown; - int targetIndex = -1; - int entryPointIndex = -1; - }; - List<RawOutput> rawOutputs; - - struct RawTarget - { - CodeGenTarget format = CodeGenTarget::Unknown; - ProfileVersion profileVersion = ProfileVersion::Unknown; - SlangTargetFlags targetFlags = 0; - int targetID = -1; - FloatingPointMode floatingPointMode = FloatingPointMode::Default; - - // State for tracking command-line errors - bool conflictingProfilesSet = false; - bool redundantProfileSet = false; - - }; - List<RawTarget> rawTargets; - - RawTarget defaultTarget; - - void addSharedLibraryPath(SharedLibraryType libType, const String& path) - { - if (!sharedLibraryLoader) - { - sharedLibraryLoader = new ConfigurableSharedLibraryLoader; - } - sharedLibraryLoader->addEntry(libType, ConfigurableSharedLibraryLoader::changePath, path); - } - - int addTranslationUnit( - SlangSourceLanguage language, - Stage impliedStage) - { - auto translationUnitIndex = rawTranslationUnits.getCount(); - auto translationUnitID = spAddTranslationUnit(compileRequest, 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; - - rawTranslationUnits.add(rawTranslationUnit); - - return int(translationUnitIndex); - } - - 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); - } - - spAddTranslationUnitSourceFile( - compileRequest, - rawTranslationUnits[slangTranslationUnitIndex].translationUnitID, - path.begin()); - - // Set the translation unit to be used by subsequent entry points - currentTranslationUnitIndex = slangTranslationUnitIndex; - } - - void addInputForeignShaderPath( - String const& path, - SlangSourceLanguage language, - Stage impliedStage) - { - translationUnitCount++; - currentTranslationUnitIndex = addTranslationUnit(language, impliedStage); - - spAddTranslationUnitSourceFile( - compileRequest, - rawTranslationUnits[currentTranslationUnitIndex].translationUnitID, - path.begin()); - } - - static Profile::RawVal 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 (int i = 0; i < SLANG_COUNT_OF(entries); ++i) - { - const Entry& entry = entries[i]; - if (path.endsWith(entry.ext)) - { - return entry.profileId; - } - } - return Profile::Unknown; - } - - static SlangSourceLanguage findSourceLanguageFromPath(const String& path, Stage& outImpliedStage) - { - struct Entry - { - const char* ext; - SlangSourceLanguage sourceLanguage; - SlangStage impliedStage; - }; - - static const Entry entries[] = - { - { ".slang", SLANG_SOURCE_LANGUAGE_SLANG, SLANG_STAGE_NONE }, - - { ".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 }, - }; - - for (int i = 0; i < SLANG_COUNT_OF(entries); ++i) - { - const Entry& entry = entries[i]; - if (path.endsWith(entry.ext)) - { - outImpliedStage = Stage(entry.impliedStage); - return entry.sourceLanguage; - } - } - return SLANG_SOURCE_LANGUAGE_UNKNOWN; - } - - SlangResult addInputPath( - char const* inPath) - { - inputPathCount++; - - // look at the extension on the file name to determine - // how we should handle it. - String path = String(inPath); - - if( path.endsWith(".slang") ) - { - // Plain old slang code - addInputSlangPath(path); - return SLANG_OK; - } - - Stage impliedStage = Stage::Unknown; - SlangSourceLanguage sourceLanguage = findSourceLanguageFromPath(path, impliedStage); - - if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN) - { - requestImpl->getSink()->diagnose(SourceLoc(), Diagnostics::cannotDeduceSourceLanguage, inPath); - return SLANG_FAIL; - } - - addInputForeignShaderPath(path, sourceLanguage, impliedStage); - - return SLANG_OK; - } - - void addOutputPath( - String const& path, - CodeGenTarget impliedFormat) - { - RawOutput rawOutput; - rawOutput.path = path; - rawOutput.impliedFormat = impliedFormat; - rawOutputs.add(rawOutput); - } - - void addOutputPath(char const* inPath) - { - String path = String(inPath); - - if (!inPath) {} -#define CASE(EXT, TARGET) \ - else if(path.endsWith(EXT)) do { addOutputPath(path, CodeGenTarget(SLANG_##TARGET)); } while(0) - - CASE(".hlsl", HLSL); - CASE(".fx", HLSL); - - CASE(".dxbc", DXBC); - CASE(".dxbc.asm", DXBC_ASM); - - CASE(".dxil", DXIL); - CASE(".dxil.asm", DXIL_ASM); - - CASE(".glsl", GLSL); - CASE(".vert", GLSL); - CASE(".frag", GLSL); - CASE(".geom", GLSL); - CASE(".tesc", GLSL); - CASE(".tese", GLSL); - CASE(".comp", GLSL); - - CASE(".spv", SPIRV); - CASE(".spv.asm", SPIRV_ASM); - - CASE(".c", C_SOURCE); - CASE(".cpp", CPP_SOURCE); - -#undef CASE - - else if (path.endsWith(".slang-module")) - { - spSetOutputContainerFormat(compileRequest, SLANG_CONTAINER_FORMAT_SLANG_MODULE); - requestImpl->containerOutputPath = path; - } - else - { - // Allow an unknown-format `-o`, assuming we get a target format - // from another argument. - addOutputPath(path, CodeGenTarget::Unknown); - } - } - - RawEntryPoint* getCurrentEntryPoint() - { - auto rawEntryPointCount = rawEntryPoints.getCount(); - return rawEntryPointCount ? &rawEntryPoints[rawEntryPointCount-1] : &defaultEntryPoint; - } - - void setStage(RawEntryPoint* rawEntryPoint, Stage stage) - { - if(rawEntryPoint->stage != Stage::Unknown) - { - rawEntryPoint->redundantStageSet = true; - if( stage != rawEntryPoint->stage ) - { - rawEntryPoint->conflictingStagesSet = true; - } - } - rawEntryPoint->stage = stage; - } - - RawTarget* getCurrentTarget() - { - auto rawTargetCount = rawTargets.getCount(); - return rawTargetCount ? &rawTargets[rawTargetCount-1] : &defaultTarget; - } - - void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion) - { - if(rawTarget->profileVersion != ProfileVersion::Unknown) - { - rawTarget->redundantProfileSet = true; - - if(profileVersion != rawTarget->profileVersion) - { - rawTarget->conflictingProfilesSet = true; - } - } - rawTarget->profileVersion = profileVersion; - } - - void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode) - { - rawTarget->floatingPointMode = mode; - } - - 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* sink = requestImpl->getSink(); - - SlangMatrixLayoutMode defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_MODE_UNKNOWN; - - char const* const* argCursor = &argv[0]; - char const* const* argEnd = &argv[argc]; - while (argCursor != argEnd) - { - char const* arg = *argCursor++; - if (arg[0] == '-') - { - String argStr = String(arg); - - if(argStr == "-no-mangle" ) - { - flags |= SLANG_COMPILE_FLAG_NO_MANGLING; - } - else if (argStr == "-no-codegen") - { - flags |= SLANG_COMPILE_FLAG_NO_CODEGEN; - } - else if(argStr == "-dump-ir" ) - { - requestImpl->getFrontEndReq()->shouldDumpIR = true; - requestImpl->getBackEndReq()->shouldDumpIR = true; - } - else if (argStr == "-serial-ir") - { - requestImpl->getFrontEndReq()->useSerialIRBottleneck = true; - } - else if (argStr == "-verbose-paths") - { - requestImpl->getSink()->flags |= DiagnosticSink::Flag::VerbosePath; - } - else if (argStr == "-verify-debug-serial-ir") - { - requestImpl->getFrontEndReq()->verifyDebugSerialization = true; - } - else if(argStr == "-validate-ir" ) - { - requestImpl->getFrontEndReq()->shouldValidateIR = true; - requestImpl->getBackEndReq()->shouldValidateIR = true; - } - else if(argStr == "-skip-codegen" ) - { - requestImpl->shouldSkipCodegen = true; - } - else if(argStr == "-parameter-blocks-use-register-spaces" ) - { - getCurrentTarget()->targetFlags |= SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES; - } - else if (argStr == "-target") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - SlangCompileTarget format = SLANG_TARGET_UNKNOWN; - - #define CASE(NAME, TARGET) \ - if(name == NAME) { format = SLANG_##TARGET; } else - - CASE("hlsl", HLSL) - CASE("glsl", GLSL) - CASE("dxbc", DXBC) - CASE("dxbc-assembly", DXBC_ASM) - CASE("dxbc-asm", DXBC_ASM) - CASE("spirv", SPIRV) - CASE("spirv-assembly", SPIRV_ASM) - CASE("spirv-asm", SPIRV_ASM) - CASE("dxil", DXIL) - CASE("dxil-assembly", DXIL_ASM) - CASE("dxil-asm", DXIL_ASM) - CASE("c", C_SOURCE) - CASE("cpp", CPP_SOURCE) - - #undef CASE - /* else */ - { - sink->diagnose(SourceLoc(), Diagnostics::unknownCodeGenerationTarget, name); - return SLANG_FAIL; - } - - RawTarget rawTarget; - rawTarget.format = CodeGenTarget(format); - - rawTargets.add(rawTarget); - } - // 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 (argStr == "-profile") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - SlangProfileID profileID = spFindProfile(session, name.begin()); - if( profileID == SLANG_PROFILE_UNKNOWN ) - { - sink->diagnose(SourceLoc(), Diagnostics::unknownProfile, name); - return SLANG_FAIL; - } - else - { - auto profile = Profile(profileID); - - setProfileVersion(getCurrentTarget(), profile.GetVersion()); - - // 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); - } - } - } - else if (argStr == "-stage") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - Stage stage = findStageByName(name); - if( stage == Stage::Unknown ) - { - sink->diagnose(SourceLoc(), Diagnostics::unknownStage, name); - return SLANG_FAIL; - } - else - { - setStage(getCurrentEntryPoint(), stage); - } - } - else if (argStr == "-entry") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - RawEntryPoint rawEntryPoint; - rawEntryPoint.name = name; - rawEntryPoint.translationUnitIndex = currentTranslationUnitIndex; - - rawEntryPoints.add(rawEntryPoint); - } - else if (argStr == "-pass-through") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - SlangPassThrough passThrough = SLANG_PASS_THROUGH_NONE; - if (name == "fxc") { passThrough = SLANG_PASS_THROUGH_FXC; } - else if (name == "dxc") { passThrough = SLANG_PASS_THROUGH_DXC; } - else if (name == "glslang") { passThrough = SLANG_PASS_THROUGH_GLSLANG; } - else - { - sink->diagnose(SourceLoc(), Diagnostics::unknownPassThroughTarget, name); - return SLANG_FAIL; - } - - spSetPassThrough( - compileRequest, - passThrough); - } - else if (argStr == "-dxc-path") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - addSharedLibraryPath(SharedLibraryType::Dxc, name); - addSharedLibraryPath(SharedLibraryType::Dxil, name); - } - else if (argStr == "-glslang-path") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - addSharedLibraryPath(SharedLibraryType::Glslang, name); - } - else if (argStr == "-fxc-path") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - addSharedLibraryPath(SharedLibraryType::Fxc, name); - } - else if (argStr[1] == 'D') - { - // The value to be defined might be part of the same option, as in: - // -DFOO - // or it might come separately, as in: - // -D FOO - char const* defineStr = arg + 2; - if (defineStr[0] == 0) - { - // Need to read another argument from the command line - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgumentRaw(sink, arg, &argCursor, argEnd, &defineStr)); - } - // The string that sets up the define can have an `=` between - // the name to be defined and its value, so we search for one. - char const* eqPos = nullptr; - for(char const* dd = defineStr; *dd; ++dd) - { - if (*dd == '=') - { - eqPos = dd; - break; - } - } - - // Now set the preprocessor define - // - if (eqPos) - { - // If we found an `=`, we split the string... - - spAddPreprocessorDefine( - compileRequest, - String(defineStr, eqPos).begin(), - String(eqPos+1).begin()); - } - else - { - // If there was no `=`, then just #define it to an empty string - - spAddPreprocessorDefine( - compileRequest, - String(defineStr).begin(), - ""); - } - } - else if (argStr[1] == 'I') - { - // 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) - char const* includeDirStr = arg + 2; - if (includeDirStr[0] == 0) - { - // Need to read another argument from the command line - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgumentRaw(sink, arg, &argCursor, argEnd, &includeDirStr)); - } - - spAddSearchPath( - compileRequest, - String(includeDirStr).begin()); - } - // - // A `-o` option is used to specify a desired output file. - else if (argStr == "-o") - { - char const* outputPath = nullptr; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgumentRaw(sink, arg, &argCursor, argEnd, &outputPath)); - if (!outputPath) continue; - - addOutputPath(outputPath); - } - else if(argStr == "-matrix-layout-row-major") - { - defaultMatrixLayoutMode = kMatrixLayoutMode_RowMajor; - } - else if(argStr == "-matrix-layout-column-major") - { - defaultMatrixLayoutMode = kMatrixLayoutMode_ColumnMajor; - } - else if(argStr == "-line-directive-mode") - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - SlangLineDirectiveMode mode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT; - if(name == "none") - { - mode = SLANG_LINE_DIRECTIVE_MODE_NONE; - } - else - { - sink->diagnose(SourceLoc(), Diagnostics::unknownLineDirectiveMode, name); - return SLANG_FAIL; - } - - spSetLineDirectiveMode(compileRequest, mode); - - } - else if( argStr == "-fp-mode" || argStr == "-floating-point-mode" ) - { - String name; - SLANG_RETURN_ON_FAIL(tryReadCommandLineArgument(sink, arg, &argCursor, argEnd, name)); - - FloatingPointMode mode = FloatingPointMode::Default; - if(name == "fast") - { - mode = FloatingPointMode::Fast; - } - else if(name == "precise") - { - mode = FloatingPointMode::Precise; - } - else - { - sink->diagnose(SourceLoc(), Diagnostics::unknownFloatingPointMode, name); - return SLANG_FAIL; - } - - setFloatingPointMode(getCurrentTarget(), mode); - } - else if( argStr[1] == 'O' ) - { - char const* name = arg + 2; - SlangOptimizationLevel level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; - - bool invalidOptimizationLevel = strlen(name) > 2; - switch( name[0] ) - { - case '0': level = SLANG_OPTIMIZATION_LEVEL_NONE; break; - case '1': level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; break; - case '2': level = SLANG_OPTIMIZATION_LEVEL_HIGH; break; - case '3': level = SLANG_OPTIMIZATION_LEVEL_MAXIMAL; break; - case 0 : level = SLANG_OPTIMIZATION_LEVEL_DEFAULT; break; - default: - invalidOptimizationLevel = true; - break; - } - if( invalidOptimizationLevel ) - { - sink->diagnose(SourceLoc(), Diagnostics::unknownOptimiziationLevel, name); - return SLANG_FAIL; - } - - spSetOptimizationLevel(compileRequest, level); - } - - // 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( argStr == "-g" || argStr == "-g2" ) - { - spSetDebugInfoLevel(compileRequest, SLANG_DEBUG_INFO_LEVEL_STANDARD); - } - else if( argStr == "-g0" ) - { - spSetDebugInfoLevel(compileRequest, SLANG_DEBUG_INFO_LEVEL_NONE); - } - else if( argStr == "-g1" ) - { - spSetDebugInfoLevel(compileRequest, SLANG_DEBUG_INFO_LEVEL_MINIMAL); - } - else if( argStr == "-g3" ) - { - spSetDebugInfoLevel(compileRequest, SLANG_DEBUG_INFO_LEVEL_MAXIMAL); - } - else if( argStr == "-default-image-format-unknown" ) - { - requestImpl->getBackEndReq()->useUnknownImageFormatAsDefault = true; - } - else if (argStr == "--") - { - // The `--` option causes us to stop trying to parse options, - // and treat the rest of the command line as input file names: - while (argCursor != argEnd) - { - SLANG_RETURN_ON_FAIL(addInputPath(*argCursor++)); - } - break; - } - else - { - sink->diagnose(SourceLoc(), Diagnostics::unknownCommandLineOption, argStr); - // TODO: print a usage message - return SLANG_FAIL; - } - } - else - { - SLANG_RETURN_ON_FAIL(addInputPath(arg)); - } - } - - spSetCompileFlags(compileRequest, 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( rawEntryPoints.getCount() == 1 ) - { - if(defaultEntryPoint.stage != Stage::Unknown) - { - setStage(getCurrentEntryPoint(), defaultEntryPoint.stage); - } - - 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 ) - { - if( rawEntryPoints.getCount() == 0 ) - { - sink->diagnose(SourceLoc(), Diagnostics::stageSpecificationIgnoredBecauseNoEntryPoints); - } - else - { - 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) - { - for( auto& entryPoint : rawEntryPoints ) - { - entryPoint.translationUnitIndex = 0; - } - } - else - { - // 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; - - anyEntryPointWithoutTranslationUnit = true; - } - if( anyEntryPointWithoutTranslationUnit ) - { - sink->diagnose(SourceLoc(), Diagnostics::entryPointsNeedToBeAssociatedWithTranslationUnits); - return SLANG_FAIL; - } - } - - // 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 ) - { - // Skip entry points that already have a stage. - if(rawEntryPoint.stage != Stage::Unknown) - continue; - - // Sanity check: don't process entry points with no associated translation unit. - if( rawEntryPoint.translationUnitIndex == -1 ) - continue; - - auto impliedStage = rawTranslationUnits[rawEntryPoint.translationUnitIndex].impliedStage; - if(impliedStage != Stage::Unknown) - rawEntryPoint.stage = impliedStage; - } - - // 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 : rawEntryPoints ) - { - 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 ) - { - // 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); - } - } - } - - // 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( requestImpl->passThrough != PassThroughMode::None ) - { - for( auto& rawEntryPoint : rawEntryPoints ) - { - if( rawEntryPoint.stage == Stage::Unknown ) - { - 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; - - auto translationUnitID = rawTranslationUnits[rawEntryPoint.translationUnitIndex].translationUnitID; - - int entryPointID = spAddEntryPoint( - compileRequest, - translationUnitID, - rawEntryPoint.name.begin(), - SlangStage(rawEntryPoint.stage)); - - rawEntryPoint.entryPointID = entryPointID; - } - - // 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) - { - 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(); - - RawTarget rawTarget; - rawTarget.format = impliedFormat; - rawTargets.add(rawTarget); - - mapFormatToTargetIndex[impliedFormat] = targetIndex; - } - - rawOutput.targetIndex = targetIndex; - } - } - 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) rawTargets.getCount(); - for(int targetIndex = 0; targetIndex < targetCount; ++targetIndex) - { - auto format = rawTargets[targetIndex].format; - - if( mapFormatToTargetIndex.ContainsKey(format) ) - { - 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( rawTargets.getCount() == 0 - && defaultTarget.profileVersion != ProfileVersion::Unknown - && !defaultTarget.conflictingProfilesSet) - { - // 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; - - // For GLSL profile versions, we will assume SPIR-V - // is the output format the user intended. - case ProfileFamily::GLSL: - inferredFormat = CodeGenTarget::SPIRV; - break; - - // For DX profile versions, we will assume that the - // user wants DXIL for Shader Model 6.0 and up, - // and DXBC for all earlier versions. - // - // Note: There is overlap where both DXBC and DXIL - // nominally support SM 5.1, but in general we - // expect users to prefer to make a clean break - // at SM 6.0. Anybody who cares about the overlap - // cases should manually specify `-target dxil`. - // - case ProfileFamily::DX: - if( profileVersion >= ProfileVersion::DX_6_0 ) - { - inferredFormat = CodeGenTarget::DXIL; - } - else - { - inferredFormat = CodeGenTarget::DXBytecode; - } - break; - } - - if( inferredFormat != CodeGenTarget::Unknown ) - { - RawTarget rawTarget; - rawTarget.format = inferredFormat; - 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) - { - if(defaultTarget.profileVersion != ProfileVersion::Unknown) - { - setProfileVersion(getCurrentTarget(), defaultTarget.profileVersion); - } - - getCurrentTarget()->targetFlags |= defaultTarget.targetFlags; - - if( defaultTarget.floatingPointMode != FloatingPointMode::Default ) - { - setFloatingPointMode(getCurrentTarget(), defaultTarget.floatingPointMode); - } - } - 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( 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( rawTargets.getCount() == 0 ) - { - sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets); - } - else - { - sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets); - } - } - - if( defaultTarget.floatingPointMode != FloatingPointMode::Default ) - { - if( rawTargets.getCount() == 0 ) - { - sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseNoTargets); - } - else - { - sink->diagnose(SourceLoc(), Diagnostics::targetFlagsIgnoredBecauseBeforeAllTargets); - } - } - - } - - for(auto& rawTarget : rawTargets) - { - if( rawTarget.conflictingProfilesSet ) - { - sink->diagnose(SourceLoc(), Diagnostics::conflictingProfilesSpecifiedForTarget, rawTarget.format); - } - else if( rawTarget.redundantProfileSet ) - { - sink->diagnose(SourceLoc(), Diagnostics::sameProfileSpecifiedMoreThanOnce, rawTarget.profileVersion, rawTarget.format); - } - } - - // 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) - { - int targetID = spAddCodeGenTarget(compileRequest, SlangCompileTarget(rawTarget.format)); - rawTarget.targetID = targetID; - - if( rawTarget.profileVersion != ProfileVersion::Unknown ) - { - spSetTargetProfile(compileRequest, targetID, Profile(rawTarget.profileVersion).raw); - } - - if( rawTarget.targetFlags ) - { - spSetTargetFlags(compileRequest, targetID, rawTarget.targetFlags); - } - - if( rawTarget.floatingPointMode != FloatingPointMode::Default ) - { - spSetTargetFloatingPointMode(compileRequest, targetID, SlangFloatingPointMode(rawTarget.floatingPointMode)); - } - } - - if(defaultMatrixLayoutMode != SLANG_MATRIX_LAYOUT_MODE_UNKNOWN) - { - spSetMatrixLayoutMode(compileRequest, 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( rawEntryPoints.getCount() == 1 ) - { - for( auto& rawOutput : rawOutputs ) - { - rawOutput.entryPointIndex = 0; - } - } - // - // Similarly, if there is only one target, then all outputs must - // implicitly appertain to that target. - // - if( rawTargets.getCount() == 1 ) - { - for( auto& rawOutput : rawOutputs ) - { - rawOutput.targetIndex = 0; - } - } - - // Consider the output files specified via `-o` and try to figure - // out how to deal with them. - // - for(auto& rawOutput : rawOutputs) - { - // For now, all output formats need to be tightly bound to - // both a target and an entry point (down the road we will - // need to support output formats that can store multiple - // entry points in one file). - - // If an output doesn't have a target associated with - // it, then search for the target with the matching format. - if( rawOutput.targetIndex == -1 ) - { - 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); - } - } - - // 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 ) - { - sink->diagnose(SourceLoc(), Diagnostics::cannotMatchOutputFileToEntryPoint, rawOutput.path); - } - } - - // 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) - { - if(rawOutput.targetIndex == -1) continue; - if(rawOutput.entryPointIndex == -1) continue; - - auto targetID = rawTargets[rawOutput.targetIndex].targetID; - Int entryPointID = rawEntryPoints[rawOutput.entryPointIndex].entryPointID; - - auto target = requestImpl->getLinkage()->targets[targetID]; - auto entryPointReq = requestImpl->getFrontEndReq()->getEntryPointReqs()[entryPointID]; - - RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo; - if( !requestImpl->targetInfos.TryGetValue(target, targetInfo) ) - { - targetInfo = new EndToEndCompileRequest::TargetInfo(); - requestImpl->targetInfos[target] = targetInfo; - } - - String outputPath; - if( targetInfo->entryPointOutputPaths.ContainsKey(entryPointID) ) - { - sink->diagnose(SourceLoc(), Diagnostics::duplicateOutputPathsForEntryPointAndTarget, entryPointReq->getName(), target->getTarget()); - } - else - { - targetInfo->entryPointOutputPaths[entryPointID] = rawOutput.path; - } - } - - if (sharedLibraryLoader) - { - spSessionSetSharedLibraryLoader(session, sharedLibraryLoader); - } - - return (sink->GetErrorCount() == 0) ? SLANG_OK : SLANG_FAIL; - } -}; - - -SlangResult parseOptions( - SlangCompileRequest* compileRequestIn, - int argc, - char const* const* argv) -{ - Slang::EndToEndCompileRequest* compileRequest = (Slang::EndToEndCompileRequest*) compileRequestIn; - - OptionsParser parser; - parser.compileRequest = compileRequestIn; - parser.requestImpl = compileRequest; - parser.session = (SlangSession*)compileRequest->getSession(); - - Result res = parser.parse(argc, argv); - - DiagnosticSink* sink = compileRequest->getSink(); - if (sink->GetErrorCount() > 0) - { - // Put the errors in the diagnostic - compileRequest->mDiagnosticOutput = sink->outputBuffer.ProduceString(); - } - - return res; -} - - -} // namespace Slang - -SLANG_API SlangResult spProcessCommandLineArguments( - SlangCompileRequest* request, - char const* const* args, - int argCount) -{ - return Slang::parseOptions(request, argCount, args); -} |
