| //===--- HLSLOptions.cpp - Driver Options Table ---------------------------===// |
| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // HLSLOptions.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "dxc/Support/WinIncludes.h" |
| |
| #include "dxc/Support/dxcapi.use.h" |
| |
| #include "dxc/DXIL/DxilShaderModel.h" |
| #include "dxc/DxilContainer/DxilContainer.h" |
| #include "dxc/Support/Global.h" |
| #include "dxc/Support/HLSLOptions.h" |
| #include "dxc/Support/Unicode.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/Option/OptTable.h" |
| #include "llvm/Option/Option.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace llvm::opt; |
| using namespace dxc; |
| using namespace hlsl; |
| using namespace hlsl::options; |
| |
| #define PREFIX(NAME, VALUE) static const char *const NAME[] = VALUE; |
| #include "dxc/Support/HLSLOptions.inc" |
| #undef PREFIX |
| |
| static const OptTable::Info HlslInfoTable[] = { |
| #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ |
| HELPTEXT, METAVAR) \ |
| {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \ |
| PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS}, |
| #include "dxc/Support/HLSLOptions.inc" |
| #undef OPTION |
| }; |
| |
| namespace { |
| |
| class HlslOptTable : public OptTable { |
| public: |
| HlslOptTable() |
| : OptTable(HlslInfoTable, llvm::array_lengthof(HlslInfoTable)) {} |
| }; |
| |
| } // namespace |
| |
| static HlslOptTable *g_HlslOptTable; |
| |
| std::error_code hlsl::options::initHlslOptTable() { |
| DXASSERT(g_HlslOptTable == nullptr, "else double-init"); |
| g_HlslOptTable = new (std::nothrow) HlslOptTable(); |
| if (g_HlslOptTable == nullptr) |
| return std::error_code(E_OUTOFMEMORY, std::system_category()); |
| return std::error_code(); |
| } |
| |
| void hlsl::options::cleanupHlslOptTable() { |
| delete g_HlslOptTable; |
| g_HlslOptTable = nullptr; |
| } |
| |
| const OptTable *hlsl::options::getHlslOptTable() { return g_HlslOptTable; } |
| |
| void DxcDefines::push_back(llvm::StringRef value) { |
| // Skip empty defines. |
| if (value.size() > 0) { |
| DefineStrings.push_back(value); |
| } |
| } |
| |
| UINT32 DxcDefines::ComputeNumberOfWCharsNeededForDefines() { |
| UINT32 wcharSize = 0; |
| for (llvm::StringRef &S : DefineStrings) { |
| DXASSERT(S.size() > 0, |
| "else DxcDefines::push_back should not have added this"); |
| const int wideLength = ::MultiByteToWideChar( |
| CP_UTF8, MB_ERR_INVALID_CHARS, S.data(), S.size(), nullptr, 0); |
| IFTARG(wideLength != 0); |
| wcharSize += wideLength + 1; // adding null terminated character |
| } |
| return wcharSize; |
| } |
| |
| void DxcDefines::BuildDefines() { |
| // Calculate and prepare the size of the backing buffer. |
| DXASSERT(DefineValues == nullptr, "else DxcDefines is already built"); |
| UINT32 wcharSize = ComputeNumberOfWCharsNeededForDefines(); |
| |
| DefineValues = new wchar_t[wcharSize]; |
| DefineVector.resize(DefineStrings.size()); |
| |
| // Build up the define structures while filling in the backing buffer. |
| UINT32 remaining = wcharSize; |
| LPWSTR pWriteCursor = DefineValues; |
| for (size_t i = 0; i < DefineStrings.size(); ++i) { |
| llvm::StringRef &S = DefineStrings[i]; |
| DxcDefine &D = DefineVector[i]; |
| const int wideLength = |
| ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, S.data(), S.size(), |
| pWriteCursor, remaining); |
| DXASSERT(wideLength > 0, |
| "else it should have failed during size calculation"); |
| LPWSTR pDefineEnd = pWriteCursor + wideLength; |
| D.Name = pWriteCursor; |
| |
| LPWSTR pEquals = std::find(pWriteCursor, pDefineEnd, L'='); |
| if (pEquals == pDefineEnd) { |
| D.Value = nullptr; |
| } else { |
| *pEquals = L'\0'; |
| D.Value = pEquals + 1; |
| } |
| |
| // Advance past converted characters and include the null terminator. |
| pWriteCursor += wideLength; |
| *pWriteCursor = L'\0'; |
| ++pWriteCursor; |
| |
| DXASSERT(pWriteCursor <= DefineValues + wcharSize, |
| "else this function is calculating this incorrectly"); |
| remaining -= (wideLength + 1); |
| } |
| } |
| |
| bool DxcOpts::IsRootSignatureProfile() const { |
| return TargetProfile == "rootsig_1_0" || TargetProfile == "rootsig_1_1"; |
| } |
| |
| bool DxcOpts::IsLibraryProfile() const { |
| return TargetProfile.startswith("lib_"); |
| } |
| |
| bool DxcOpts::GenerateFullDebugInfo() const { return DebugInfo; } |
| |
| bool DxcOpts::GeneratePDB() const { return DebugInfo || SourceOnlyDebug; } |
| |
| bool DxcOpts::EmbedDebugInfo() const { return EmbedDebug; } |
| |
| bool DxcOpts::EmbedPDBName() const { |
| return GeneratePDB() || !DebugFile.empty(); |
| } |
| |
| bool DxcOpts::DebugFileIsDirectory() const { |
| return !DebugFile.empty() && |
| llvm::sys::path::is_separator(DebugFile[DebugFile.size() - 1]); |
| } |
| |
| llvm::StringRef DxcOpts::GetPDBName() const { |
| if (!DebugFileIsDirectory()) |
| return DebugFile; |
| return llvm::StringRef(); |
| } |
| |
| MainArgs::MainArgs(int argc, const wchar_t **argv, int skipArgCount) { |
| if (argc > skipArgCount) { |
| Utf8StringVector.reserve(argc - skipArgCount); |
| Utf8CharPtrVector.reserve(argc - skipArgCount); |
| for (int i = skipArgCount; i < argc; ++i) { |
| Utf8StringVector.emplace_back(Unicode::WideToUTF8StringOrThrow(argv[i])); |
| Utf8CharPtrVector.push_back(Utf8StringVector.back().data()); |
| } |
| } |
| } |
| |
| MainArgs::MainArgs(int argc, const char **argv, int skipArgCount) { |
| if (argc > skipArgCount) { |
| Utf8StringVector.reserve(argc - skipArgCount); |
| Utf8CharPtrVector.reserve(argc - skipArgCount); |
| for (int i = skipArgCount; i < argc; ++i) { |
| Utf8StringVector.emplace_back(argv[i]); |
| Utf8CharPtrVector.push_back(Utf8StringVector.back().data()); |
| } |
| } |
| } |
| |
| MainArgs::MainArgs(llvm::ArrayRef<llvm::StringRef> args) { |
| Utf8StringVector.reserve(args.size()); |
| Utf8CharPtrVector.reserve(args.size()); |
| for (llvm::StringRef str : args) { |
| Utf8StringVector.emplace_back(str.str()); |
| Utf8CharPtrVector.push_back(Utf8StringVector.back().data()); |
| } |
| } |
| |
| MainArgs &MainArgs::operator=(const MainArgs &other) { |
| Utf8StringVector.clear(); |
| Utf8CharPtrVector.clear(); |
| Utf8StringVector.reserve(other.Utf8StringVector.size()); |
| Utf8CharPtrVector.reserve(other.Utf8StringVector.size()); |
| for (const std::string &str : other.Utf8StringVector) { |
| Utf8StringVector.emplace_back(str); |
| Utf8CharPtrVector.push_back(Utf8StringVector.back().data()); |
| } |
| return *this; |
| } |
| |
| StringRefWide::StringRefWide(llvm::StringRef value) { |
| if (!value.empty()) |
| m_value = Unicode::UTF8ToWideStringOrThrow(value.data()); |
| } |
| |
| static bool GetTargetVersionFromString(llvm::StringRef ref, unsigned *major, |
| unsigned *minor) { |
| *major = *minor = -1; |
| unsigned len = ref.size(); |
| if (len < 6 || len > 11) // length: ps_6_0 to rootsig_1_0 |
| return false; |
| if (ref[len - 4] != '_' || ref[len - 2] != '_') |
| return false; |
| |
| char cMajor = ref[len - 3]; |
| char cMinor = ref[len - 1]; |
| |
| if (cMajor >= '0' && cMajor <= '9') |
| *major = cMajor - '0'; |
| else |
| return false; |
| |
| if (cMinor == 'x') |
| *minor = 0xF; |
| else if (cMinor >= '0' && cMinor <= '9') |
| *minor = cMinor - '0'; |
| else |
| return false; |
| |
| return true; |
| } |
| |
| // Copied from CompilerInvocation since we parse our own diagnostic arguments |
| static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group, |
| OptSpecifier GroupWithValue, |
| std::vector<std::string> &Diagnostics) { |
| for (Arg *A : Args.filtered(Group)) { |
| if (A->getOption().getKind() == Option::FlagClass) { |
| // The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add |
| // its name (minus the "W" or "R" at the beginning) to the warning list. |
| Diagnostics.push_back(A->getOption().getName().drop_front(1)); |
| } else if (A->getOption().matches(GroupWithValue)) { |
| // This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic |
| // group. |
| Diagnostics.push_back(A->getOption().getName().drop_front(1).rtrim("=-")); |
| } else { |
| // Otherwise, add its value (for OPT_W_Joined and similar). |
| for (const char *Arg : A->getValues()) |
| Diagnostics.emplace_back(Arg); |
| } |
| } |
| } |
| |
| static std::pair<std::string, std::string> ParseDefine(std::string &argVal) { |
| std::pair<std::string, std::string> result = std::make_pair("", ""); |
| if (argVal.empty()) |
| return result; |
| auto defEndPos = |
| argVal.find('=') == std::string::npos ? argVal.size() : argVal.find('='); |
| result.first = argVal.substr(0, defEndPos); |
| if (!result.first.empty() && defEndPos < argVal.size() - 1) { |
| result.second = argVal.substr(defEndPos + 1, argVal.size() - defEndPos - 1); |
| } |
| return result; |
| } |
| |
| // SPIRV Change Starts |
| #ifdef ENABLE_SPIRV_CODEGEN |
| /// Checks and collects the arguments for -fvk-{b|s|t|u}-shift into *shifts. |
| static bool handleVkShiftArgs(const InputArgList &args, OptSpecifier id, |
| const char *name, |
| llvm::SmallVectorImpl<int32_t> *shifts, |
| llvm::raw_ostream &errors) { |
| const auto values = args.getAllArgValues(id); |
| |
| if (values.empty()) |
| return true; |
| |
| if (!args.hasArg(OPT_spirv)) { |
| errors << "-fvk-" << name << "-shift requires -spirv"; |
| return false; |
| } |
| |
| if (!args.getLastArgValue(OPT_fvk_bind_register).empty()) { |
| errors << "-fvk-" << name |
| << "-shift cannot be used together with -fvk-bind-register"; |
| return false; |
| } |
| |
| shifts->clear(); |
| bool setForAll = false; |
| |
| for (const auto &val : values) { |
| int32_t number = 0; |
| if (val == "all") { |
| number = -1; |
| setForAll = true; |
| } else { |
| if (llvm::StringRef(val).getAsInteger(10, number)) { |
| errors << "invalid -fvk-" << name << "-shift argument: " << val; |
| return false; |
| } |
| if (number < 0) { |
| errors << "negative -fvk-" << name << "-shift argument: " << val; |
| return false; |
| } |
| } |
| shifts->push_back(number); |
| } |
| if (setForAll && shifts->size() > 2) { |
| errors << "setting all sets via -fvk-" << name |
| << "-shift argument should be used alone"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Check if any options that are unsupported with SPIR-V are used. |
| static bool hasUnsupportedSpirvOption(const InputArgList &args, |
| llvm::raw_ostream &errors) { |
| // Note: The options checked here are non-exhaustive. A thorough audit of |
| // available options and their current compatibility is needed to generate a |
| // complete list. |
| std::vector<OptSpecifier> unsupportedOpts = {OPT_Fd, OPT_Fre, OPT_Gec, |
| OPT_Gis, OPT_Qstrip_reflect}; |
| |
| for (const auto &id : unsupportedOpts) { |
| if (Arg *arg = args.getLastArg(id)) { |
| errors << "-" << arg->getOption().getName() |
| << " is not supported with -spirv"; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| namespace { |
| |
| /// Maximum size of OpString instruction minus two operands |
| static const uint32_t kDefaultMaximumSourceLength = 0xFFFDu; |
| static const uint32_t kTestingMaximumSourceLength = 13u; |
| |
| } // namespace |
| #endif // ENABLE_SPIRV_CODEGEN |
| // SPIRV Change Ends |
| |
| namespace hlsl { |
| |
| LangStd parseHLSLVersion(llvm::StringRef Ver) { |
| return llvm::StringSwitch<hlsl::LangStd>(Ver) |
| .Case("2015", hlsl::LangStd::v2015) |
| .Case("2016", hlsl::LangStd::v2016) |
| .Case("2017", hlsl::LangStd::v2017) |
| .Case("2018", hlsl::LangStd::v2018) |
| .Case("2021", hlsl::LangStd::v2021) |
| .Case("202x", hlsl::LangStd::v202x) |
| .Default(hlsl::LangStd::vError); |
| } |
| namespace options { |
| |
| /// Reads all options from the given argument strings, populates opts, and |
| /// validates reporting errors and warnings. |
| int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude, |
| const MainArgs &argStrings, DxcOpts &opts, |
| llvm::raw_ostream &errors) { |
| DXASSERT_NOMSG(optionTable != nullptr); |
| opts.DefaultTextCodePage = DXC_CP_UTF8; |
| |
| unsigned missingArgIndex = 0, missingArgCount = 0; |
| InputArgList Args = |
| optionTable->ParseArgs(argStrings.getArrayRef(), missingArgIndex, |
| missingArgCount, flagsToInclude); |
| |
| // Set DefaultTextCodePage early so it may influence error buffer |
| // Default to UTF8 for compatibility |
| llvm::StringRef encoding = Args.getLastArgValue(OPT_encoding); |
| if (!encoding.empty()) { |
| if (encoding.equals_lower("utf8")) { |
| opts.DefaultTextCodePage = DXC_CP_UTF8; |
| #ifdef _WIN32 |
| } else if (encoding.equals_lower("utf16")) { |
| opts.DefaultTextCodePage = DXC_CP_UTF16; // Only on Windows |
| #else |
| } else if (encoding.equals_lower("utf32")) { |
| opts.DefaultTextCodePage = DXC_CP_UTF32; // Only on *nix |
| #endif |
| } else if (encoding.equals_lower("wide")) { |
| opts.DefaultTextCodePage = DXC_CP_WIDE; |
| } else { |
| errors << "Unsupported value '" << encoding |
| << "for -encoding option. Allowed values: wide, utf8, " |
| #ifdef _WIN32 |
| "utf16."; |
| #else |
| "utf32."; |
| #endif |
| return 1; |
| } |
| } |
| |
| // Verify consistency for external library support. |
| opts.ExternalLib = Args.getLastArgValue(OPT_external_lib); |
| opts.ExternalFn = Args.getLastArgValue(OPT_external_fn); |
| if (opts.ExternalLib.empty()) { |
| if (!opts.ExternalFn.empty()) { |
| errors << "External function cannot be specified without an external " |
| "library name."; |
| return 1; |
| } |
| } else { |
| if (opts.ExternalFn.empty()) { |
| errors << "External library name requires specifying an external " |
| "function name."; |
| return 1; |
| } |
| } |
| |
| opts.ShowHelp = Args.hasFlag(OPT_help, OPT_INVALID, false); |
| opts.ShowHelp |= (opts.ShowHelpHidden = |
| Args.hasFlag(OPT__help_hidden, OPT_INVALID, false)); |
| if (opts.ShowHelp) { |
| return 0; |
| } |
| |
| opts.ShowVersion = Args.hasFlag(OPT__version, OPT_INVALID, false); |
| if (opts.ShowVersion) { |
| return 0; |
| } |
| |
| if (missingArgCount) { |
| errors << "Argument to '" << Args.getArgString(missingArgIndex) |
| << "' is missing."; |
| return 1; |
| } |
| |
| if (!Args.hasArg(hlsl::options::OPT_Qunused_arguments)) { |
| for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { |
| errors << "Unknown argument: '" << A->getAsString(Args).c_str() << "'"; |
| return 1; |
| } |
| } |
| |
| // Add macros from the command line. |
| |
| for (const Arg *A : Args.filtered(OPT_D)) { |
| opts.Defines.push_back(A->getValue()); |
| // If supporting OPT_U and included in filter, handle undefs. |
| } |
| opts.Defines |
| .BuildDefines(); // Must be called after all defines are pushed back |
| |
| DXASSERT(opts.ExternalLib.empty() == opts.ExternalFn.empty(), |
| "else flow above is incorrect"); |
| |
| opts.PreciseOutputs = Args.getAllArgValues(OPT_precise_output); |
| |
| // when no-warnings option is present, do not output warnings. |
| opts.OutputWarnings = Args.hasFlag(OPT_INVALID, OPT_no_warnings, true); |
| opts.EntryPoint = Args.getLastArgValue(OPT_entrypoint); |
| // Entry point is required in arguments only for drivers; APIs take this |
| // through an argument. The value should default to 'main', but we let the |
| // caller apply this policy. |
| |
| if (opts.TargetProfile.empty()) { |
| opts.TargetProfile = Args.getLastArgValue(OPT_target_profile); |
| } |
| |
| if (opts.IsLibraryProfile()) { |
| // Don't bother erroring out when entry is specified. We weren't always |
| // doing this before, so doing so will break existing code. |
| // Set entry point to impossible name. |
| opts.EntryPoint = "lib.no::entry"; |
| } else { |
| if (Args.getLastArg(OPT_exports)) { |
| errors << "library profile required when using -exports option"; |
| return 1; |
| } else if (Args.hasFlag(OPT_export_shaders_only, OPT_INVALID, false)) { |
| errors |
| << "library profile required when using -export-shaders-only option"; |
| return 1; |
| } else if (Args.getLastArg(OPT_default_linkage)) { |
| errors << "library profile required when using -default-linkage option"; |
| return 1; |
| } |
| } |
| |
| opts.EnableDX9CompatMode = Args.hasFlag(OPT_Gec, OPT_INVALID, false); |
| llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version); |
| if (ver.empty()) { |
| if (opts.EnableDX9CompatMode) |
| opts.HLSLVersion = hlsl::LangStd::v2016; // Default to max supported |
| // version with /Gec flag |
| else |
| opts.HLSLVersion = hlsl::LangStd::vLatest; // Default to latest version |
| } else { |
| opts.HLSLVersion = parseHLSLVersion(ver); |
| if (opts.HLSLVersion == hlsl::LangStd::vError) { |
| errors << "Unknown HLSL version: " << ver |
| << ". Valid versions: " << hlsl::ValidVersionsStr; |
| return 1; |
| } |
| } |
| |
| if (opts.HLSLVersion == hlsl::LangStd::v2015 && |
| !(flagsToInclude & HlslFlags::ISenseOption)) { |
| errors << "HLSL Version 2015 is only supported for language services"; |
| return 1; |
| } |
| |
| if (opts.EnableDX9CompatMode && opts.HLSLVersion > hlsl::LangStd::v2016) { |
| errors << "/Gec is not supported with HLSLVersion " |
| << (unsigned long)opts.HLSLVersion; |
| return 1; |
| } |
| |
| if (opts.HLSLVersion <= hlsl::LangStd::v2016) { |
| opts.EnableFXCCompatMode = true; |
| } |
| |
| // AssemblyCodeHex not supported (Fx) |
| // OutputLibrary not supported (Fl) |
| opts.AssemblyCode = Args.getLastArgValue(OPT_Fc); |
| opts.DebugFile = Args.getLastArgValue(OPT_Fd); |
| opts.ImportBindingTable = Args.getLastArgValue(OPT_import_binding_table); |
| opts.BindingTableDefine = Args.getLastArgValue(OPT_binding_table_define); |
| opts.ExtractPrivateFile = Args.getLastArgValue(OPT_getprivate); |
| opts.Enable16BitTypes = |
| Args.hasFlag(OPT_enable_16bit_types, OPT_INVALID, false); |
| opts.OutputObject = Args.getLastArgValue(OPT_Fo); |
| opts.OutputHeader = Args.getLastArgValue(OPT_Fh); |
| opts.OutputWarningsFile = Args.getLastArgValue(OPT_Fe); |
| opts.OutputReflectionFile = Args.getLastArgValue(OPT_Fre); |
| opts.OutputRootSigFile = Args.getLastArgValue(OPT_Frs); |
| opts.OutputShaderHashFile = Args.getLastArgValue(OPT_Fsh); |
| opts.DiagnosticsFormat = |
| Args.getLastArgValue(OPT_fdiagnostics_format_EQ, "clang"); |
| opts.ShowOptionNames = Args.hasFlag(OPT_fdiagnostics_show_option, |
| OPT_fno_diagnostics_show_option, true); |
| opts.UseColor = Args.hasFlag(OPT_Cc, OPT_INVALID, false); |
| opts.UseInstructionNumbers = Args.hasFlag(OPT_Ni, OPT_INVALID, false); |
| opts.UseInstructionByteOffsets = Args.hasFlag(OPT_No, OPT_INVALID, false); |
| opts.UseHexLiterals = Args.hasFlag(OPT_Lx, OPT_INVALID, false); |
| if (Args.hasFlag(OPT_P, OPT_INVALID, false)) { |
| // Default preprocess filename is InputName.i. |
| llvm::SmallString<128> Path(Args.getLastArgValue(OPT_INPUT)); |
| llvm::sys::path::replace_extension(Path, "i"); |
| // Try to get preprocess filename from Fi. |
| opts.Preprocess = Args.getLastArgValue(OPT_Fi, Path).str(); |
| // Hack to support fxc style /P preprocess_filename. |
| // When there're more than 1 Input file, use the input which is after /P as |
| // preprocess. |
| if (!Args.hasArg(OPT_Fi)) { |
| std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT); |
| if (Inputs.size() > 1) { |
| llvm::opt::Arg *PArg = Args.getLastArg(OPT_P); |
| std::string LastInput = Inputs.back(); |
| llvm::opt::Arg *PrevInputArg = nullptr; |
| for (llvm::opt::Arg *InputArg : Args.filtered(OPT_INPUT)) { |
| // Find Input after /P. |
| if ((PArg->getIndex() + 1) == InputArg->getIndex()) { |
| opts.Preprocess = InputArg->getValue(); |
| if (LastInput == opts.Preprocess && PrevInputArg) { |
| // When InputArg is last Input, update it to other Input so |
| // Args.getLastArgValue(OPT_INPUT) get expect Input. |
| InputArg->getValues()[0] = PrevInputArg->getValues()[0]; |
| } |
| errors << "Warning: -P " << opts.Preprocess |
| << " is deprecated, please use -P -Fi " << opts.Preprocess |
| << " instead.\n"; |
| break; |
| } |
| PrevInputArg = InputArg; |
| } |
| } |
| } |
| } |
| opts.AstDumpImplicit = |
| Args.hasFlag(OPT_ast_dump_implicit, OPT_INVALID, false); |
| // -ast-dump-implicit should imply -ast-dump. |
| opts.AstDump = |
| Args.hasFlag(OPT_ast_dump, OPT_INVALID, false) || opts.AstDumpImplicit; |
| opts.WriteDependencies = |
| Args.hasFlag(OPT_write_dependencies, OPT_INVALID, false); |
| opts.OutputFileForDependencies = |
| Args.getLastArgValue(OPT_write_dependencies_to); |
| opts.DumpDependencies = |
| Args.hasFlag(OPT_dump_dependencies, OPT_INVALID, false) || |
| opts.WriteDependencies || !opts.OutputFileForDependencies.empty(); |
| opts.CodeGenHighLevel = Args.hasFlag(OPT_fcgl, OPT_INVALID, false); |
| opts.AllowPreserveValues = |
| Args.hasFlag(OPT_preserve_intermediate_values, OPT_INVALID, false); |
| opts.DebugInfo = Args.hasFlag(OPT__SLASH_Zi, OPT_INVALID, false); |
| opts.DebugNameForBinary = Args.hasFlag(OPT_Zsb, OPT_INVALID, false); |
| opts.DebugNameForSource = Args.hasFlag(OPT_Zss, OPT_INVALID, false); |
| opts.VariableName = Args.getLastArgValue(OPT_Vn); |
| opts.InputFile = Args.getLastArgValue(OPT_INPUT); |
| opts.ForceRootSigVer = Args.getLastArgValue(OPT_force_rootsig_ver); |
| if (opts.ForceRootSigVer.empty()) |
| opts.ForceRootSigVer = Args.getLastArgValue(OPT_force_rootsig_ver_); |
| opts.PrivateSource = Args.getLastArgValue(OPT_setprivate); |
| opts.RootSignatureSource = Args.getLastArgValue(OPT_setrootsignature); |
| opts.VerifyRootSignatureSource = |
| Args.getLastArgValue(OPT_verifyrootsignature); |
| opts.RootSignatureDefine = Args.getLastArgValue(OPT_rootsig_define); |
| opts.ScanLimit = 0; |
| llvm::StringRef limit = Args.getLastArgValue(OPT_memdep_block_scan_limit); |
| if (!limit.empty()) |
| opts.ScanLimit = std::stoul(std::string(limit)); |
| |
| for (std::string opt : Args.getAllArgValues(OPT_opt_disable)) |
| opts.OptToggles.Toggles[llvm::StringRef(opt).lower()] = false; |
| |
| for (std::string opt : Args.getAllArgValues(OPT_opt_enable)) { |
| std::string optimization = llvm::StringRef(opt).lower(); |
| if (opts.OptToggles.Toggles.count(optimization) && |
| !opts.OptToggles.Toggles[optimization]) { |
| errors << "Contradictory use of -opt-disable and -opt-enable with \"" |
| << llvm::StringRef(opt).lower() << "\""; |
| return 1; |
| } |
| opts.OptToggles.Toggles[optimization] = true; |
| } |
| |
| std::vector<std::string> ignoreSemDefs = |
| Args.getAllArgValues(OPT_ignore_semdef); |
| for (std::string &ignoreSemDef : ignoreSemDefs) { |
| opts.IgnoreSemDefs.insert(ignoreSemDef); |
| } |
| |
| std::vector<std::string> overrideSemDefs = |
| Args.getAllArgValues(OPT_override_semdef); |
| for (std::string &overrideSemDef : overrideSemDefs) { |
| auto kv = ParseDefine(overrideSemDef); |
| if (kv.first.empty()) |
| continue; |
| if (opts.OverrideSemDefs.find(kv.first) == opts.OverrideSemDefs.end()) { |
| opts.OverrideSemDefs.insert(std::make_pair(kv.first, kv.second)); |
| } else { |
| opts.OverrideSemDefs[kv.first] = kv.second; |
| } |
| } |
| |
| std::vector<std::string> optSelects = Args.getAllArgValues(OPT_opt_select); |
| for (unsigned i = 0; i + 1 < optSelects.size(); i += 2) { |
| std::string optimization = llvm::StringRef(optSelects[i]).lower(); |
| std::string selection = optSelects[i + 1]; |
| if (opts.OptToggles.Selects.count(optimization) && |
| selection.compare(opts.OptToggles.Selects[optimization])) { |
| errors << "Contradictory -opt-selects for \"" << optimization << "\""; |
| return 1; |
| } |
| opts.OptToggles.Selects[optimization] = selection; |
| } |
| |
| if (!opts.ForceRootSigVer.empty() && opts.ForceRootSigVer != "rootsig_1_0" && |
| opts.ForceRootSigVer != "rootsig_1_1") { |
| errors << "Unsupported value '" << opts.ForceRootSigVer |
| << "' for root signature profile."; |
| return 1; |
| } |
| |
| opts.IEEEStrict = Args.hasFlag(OPT_Gis, OPT_INVALID, false); |
| |
| opts.IgnoreLineDirectives = |
| Args.hasFlag(OPT_ignore_line_directives, OPT_INVALID, false); |
| |
| opts.FloatDenormalMode = Args.getLastArgValue(OPT_denorm); |
| // Check if a given denormalized value is valid |
| if (!opts.FloatDenormalMode.empty()) { |
| if (!(opts.FloatDenormalMode.equals_lower("preserve") || |
| opts.FloatDenormalMode.equals_lower("ftz") || |
| opts.FloatDenormalMode.equals_lower("any"))) { |
| errors << "Unsupported value '" << opts.FloatDenormalMode |
| << "' for denorm option."; |
| return 1; |
| } |
| } |
| |
| llvm::StringRef auto_binding_space = |
| Args.getLastArgValue(OPT_auto_binding_space); |
| if (!auto_binding_space.empty()) { |
| if (auto_binding_space.getAsInteger(10, opts.AutoBindingSpace)) { |
| errors << "Unsupported value '" << auto_binding_space |
| << "' for auto binding space."; |
| return 1; |
| } |
| } |
| |
| opts.Exports = Args.getAllArgValues(OPT_exports); |
| |
| opts.DefaultLinkage = Args.getLastArgValue(OPT_default_linkage); |
| if (!opts.DefaultLinkage.empty()) { |
| if (!(opts.DefaultLinkage.equals_lower("internal") || |
| opts.DefaultLinkage.equals_lower("external"))) { |
| errors << "Unsupported value '" << opts.DefaultLinkage |
| << "for -default-linkage option."; |
| return 1; |
| } |
| } |
| |
| // Check options only allowed in shader model >= 6.2FPDenormalMode |
| unsigned Major = 0; |
| unsigned Minor = 0; |
| if (!opts.TargetProfile.empty()) { |
| if (!GetTargetVersionFromString(opts.TargetProfile, &Major, &Minor)) { |
| errors << "unable to parse shader model."; |
| return 1; |
| } |
| } |
| |
| if (opts.TargetProfile.empty() || Major < 6 || (Major == 6 && Minor < 2)) { |
| if (!opts.FloatDenormalMode.empty()) { |
| errors << "denorm option is only allowed for shader model 6.2 and above."; |
| return 1; |
| } |
| } |
| |
| // /enable-16bit-types only allowed for HLSL 2018 and shader model 6.2 |
| if (opts.Enable16BitTypes) { |
| if (opts.TargetProfile.empty() || opts.HLSLVersion < hlsl::LangStd::v2018 || |
| Major < 6 || (Major == 6 && Minor < 2)) { |
| errors << "enable-16bit-types is only allowed for shader model >= 6.2 " |
| "and HLSL Language >= 2018."; |
| return 1; |
| } |
| } |
| |
| opts.DisableOptimizations = false; |
| if (Arg *A = Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3, OPT_Od)) { |
| if (A->getOption().matches(OPT_O0)) |
| opts.OptLevel = 0; |
| if (A->getOption().matches(OPT_O1)) |
| opts.OptLevel = 1; |
| if (A->getOption().matches(OPT_O2)) |
| opts.OptLevel = 2; |
| if (A->getOption().matches(OPT_O3)) |
| opts.OptLevel = 3; |
| if (A->getOption().matches(OPT_Od)) { |
| opts.DisableOptimizations = true; |
| opts.OptLevel = 0; |
| } |
| } else |
| opts.OptLevel = 3; |
| opts.OptDump = Args.hasFlag(OPT_Odump, OPT_INVALID, false); |
| |
| opts.DisableValidation = Args.hasFlag(OPT_VD, OPT_INVALID, false); |
| |
| opts.AllResourcesBound = |
| Args.hasFlag(OPT_all_resources_bound, OPT_INVALID, false); |
| opts.AllResourcesBound = Args.hasFlag(OPT_all_resources_bound_, OPT_INVALID, |
| opts.AllResourcesBound); |
| opts.IgnoreOptSemDefs = |
| Args.hasFlag(OPT_ignore_opt_semdefs, OPT_INVALID, false); |
| opts.ColorCodeAssembly = Args.hasFlag(OPT_Cc, OPT_INVALID, false); |
| opts.DefaultRowMajor = Args.hasFlag(OPT_Zpr, OPT_INVALID, false); |
| opts.DefaultColMajor = Args.hasFlag(OPT_Zpc, OPT_INVALID, false); |
| opts.DumpBin = Args.hasFlag(OPT_dumpbin, OPT_INVALID, false); |
| opts.Link = Args.hasFlag(OPT_link, OPT_INVALID, false); |
| opts.NotUseLegacyCBufLoad = |
| Args.hasFlag(OPT_no_legacy_cbuf_layout, OPT_INVALID, false); |
| opts.NotUseLegacyCBufLoad = Args.hasFlag( |
| OPT_not_use_legacy_cbuf_load_, OPT_INVALID, opts.NotUseLegacyCBufLoad); |
| opts.PackPrefixStable = |
| Args.hasFlag(OPT_pack_prefix_stable, OPT_INVALID, false); |
| opts.PackPrefixStable = |
| Args.hasFlag(OPT_pack_prefix_stable_, OPT_INVALID, opts.PackPrefixStable); |
| opts.PackOptimized = Args.hasFlag(OPT_pack_optimized, OPT_INVALID, false); |
| opts.PackOptimized = |
| Args.hasFlag(OPT_pack_optimized_, OPT_INVALID, opts.PackOptimized); |
| opts.DisplayIncludeProcess = Args.hasFlag(OPT_H, OPT_INVALID, false); |
| opts.WarningAsError = Args.hasFlag(OPT__SLASH_WX, OPT_INVALID, false); |
| opts.AvoidFlowControl = Args.hasFlag(OPT_Gfa, OPT_INVALID, false); |
| opts.PreferFlowControl = Args.hasFlag(OPT_Gfp, OPT_INVALID, false); |
| opts.RecompileFromBinary = Args.hasFlag(OPT_recompile, OPT_INVALID, false); |
| opts.StripDebug = Args.hasFlag(OPT_Qstrip_debug, OPT_INVALID, false); |
| opts.EmbedDebug = Args.hasFlag(OPT_Qembed_debug, OPT_INVALID, false); |
| opts.SourceInDebugModule = |
| Args.hasFlag(OPT_Qsource_in_debug_module, OPT_INVALID, false); |
| opts.SourceOnlyDebug = Args.hasFlag(OPT_Zs, OPT_INVALID, false); |
| opts.PdbInPrivate = Args.hasFlag(OPT_Qpdb_in_private, OPT_INVALID, false); |
| opts.StripRootSignature = |
| Args.hasFlag(OPT_Qstrip_rootsignature, OPT_INVALID, false); |
| opts.StripPrivate = Args.hasFlag(OPT_Qstrip_priv, OPT_INVALID, false); |
| opts.StripReflection = Args.hasFlag(OPT_Qstrip_reflect, OPT_INVALID, false); |
| opts.KeepReflectionInDxil = |
| Args.hasFlag(OPT_Qkeep_reflect_in_dxil, OPT_INVALID, false); |
| opts.StripReflectionFromDxil = |
| Args.hasFlag(OPT_Qstrip_reflect_from_dxil, OPT_INVALID, false); |
| opts.ExtractRootSignature = |
| Args.hasFlag(OPT_extractrootsignature, OPT_INVALID, false); |
| opts.DisassembleColorCoded = Args.hasFlag(OPT_Cc, OPT_INVALID, false); |
| opts.DisassembleInstNumbers = Args.hasFlag(OPT_Ni, OPT_INVALID, false); |
| opts.DisassembleByteOffset = Args.hasFlag(OPT_No, OPT_INVALID, false); |
| opts.DisaseembleHex = Args.hasFlag(OPT_Lx, OPT_INVALID, false); |
| opts.LegacyMacroExpansion = |
| Args.hasFlag(OPT_flegacy_macro_expansion, OPT_INVALID, false); |
| opts.LegacyResourceReservation = |
| Args.hasFlag(OPT_flegacy_resource_reservation, OPT_INVALID, false); |
| opts.ExportShadersOnly = |
| Args.hasFlag(OPT_export_shaders_only, OPT_INVALID, false); |
| opts.PrintBeforeAll = Args.hasFlag(OPT_print_before_all, OPT_INVALID, false); |
| opts.PrintAfterAll = Args.hasFlag(OPT_print_after_all, OPT_INVALID, false); |
| opts.ResMayAlias = Args.hasFlag(OPT_res_may_alias, OPT_INVALID, false); |
| opts.ResMayAlias = |
| Args.hasFlag(OPT_res_may_alias_, OPT_INVALID, opts.ResMayAlias); |
| opts.ForceZeroStoreLifetimes = |
| Args.hasFlag(OPT_force_zero_store_lifetimes, OPT_INVALID, false); |
| // Lifetime markers on by default in 6.6 unless disabled explicitly |
| opts.EnableLifetimeMarkers = |
| Args.hasFlag(OPT_enable_lifetime_markers, OPT_disable_lifetime_markers, |
| DXIL::CompareVersions(Major, Minor, 6, 6) >= 0); |
| opts.ForceDisableLocTracking = |
| Args.hasFlag(OPT_fdisable_loc_tracking, OPT_INVALID, false); |
| opts.NewInlining = |
| Args.hasFlag(OPT_fnew_inlining_behavior, OPT_INVALID, false); |
| opts.TimeReport = Args.hasFlag(OPT_ftime_report, OPT_INVALID, false); |
| opts.TimeTrace = Args.hasFlag(OPT_ftime_trace, OPT_INVALID, false) ? "-" : ""; |
| opts.VerifyDiagnostics = Args.hasFlag(OPT_verify, OPT_INVALID, false); |
| if (Args.hasArg(OPT_ftime_trace_EQ)) |
| opts.TimeTrace = Args.getLastArgValue(OPT_ftime_trace_EQ); |
| if (Arg *A = Args.getLastArg(OPT_ftime_trace_granularity_EQ)) { |
| if (llvm::StringRef(A->getValue()) |
| .getAsInteger(10, opts.TimeTraceGranularity)) { |
| opts.TimeTraceGranularity = 500; |
| errors << "Warning: Invalid value for -ftime-trace-granularity option " |
| "specified, defaulting to " |
| << opts.TimeTraceGranularity << " microseconds."; |
| } |
| } |
| |
| opts.EnablePayloadQualifiers = |
| Args.hasFlag(OPT_enable_payload_qualifiers, OPT_INVALID, |
| DXIL::CompareVersions(Major, Minor, 6, 7) >= 0); |
| |
| for (const std::string &value : Args.getAllArgValues(OPT_print_before)) { |
| opts.PrintBefore.insert(value); |
| } |
| for (const std::string &value : Args.getAllArgValues(OPT_print_after)) { |
| opts.PrintAfter.insert(value); |
| } |
| |
| if (DXIL::CompareVersions(Major, Minor, 6, 8) < 0) { |
| opts.EnablePayloadQualifiers &= |
| !Args.hasFlag(OPT_disable_payload_qualifiers, OPT_INVALID, false); |
| } |
| if (opts.EnablePayloadQualifiers && |
| DXIL::CompareVersions(Major, Minor, 6, 6) < 0) { |
| errors << "Invalid target for payload access qualifiers. Only lib_6_6 and " |
| "beyond are supported."; |
| return 1; |
| } |
| |
| opts.HandleExceptions = |
| !Args.hasFlag(OPT_disable_exception_handling, OPT_INVALID, false); |
| |
| if (opts.DefaultColMajor && opts.DefaultRowMajor) { |
| errors << "Cannot specify /Zpr and /Zpc together, use /? to get usage " |
| "information"; |
| return 1; |
| } |
| if (opts.AvoidFlowControl && opts.PreferFlowControl) { |
| errors << "Cannot specify /Gfa and /Gfp together, use /? to get usage " |
| "information"; |
| return 1; |
| } |
| if (opts.PackPrefixStable && opts.PackOptimized) { |
| errors << "Cannot specify /pack_prefix_stable and /pack_optimized " |
| "together, use /? to get usage information"; |
| return 1; |
| } |
| // TODO: more fxc option check. |
| // ERR_RES_MAY_ALIAS_ONLY_IN_CS_5 |
| // ERR_NOT_ABLE_TO_FLATTEN on if that contain side effects |
| // TODO: other front-end error. |
| // ERR_RESOURCE_NOT_IN_TEMPLATE |
| // ERR_COMPLEX_TEMPLATE_RESOURCE |
| // ERR_RESOURCE_BIND_CONFLICT |
| // ERR_TEMPLATE_VAR_CONFLICT |
| // ERR_ATTRIBUTE_PARAM_SIDE_EFFECT |
| |
| if (opts.StripPrivate && !opts.PrivateSource.empty()) { |
| errors << "Cannot specify /Qstrip_priv and /setprivate together."; |
| return 1; |
| } |
| |
| if (opts.PdbInPrivate && !opts.PrivateSource.empty()) { |
| errors << "Cannot specify /Qpdb_in_private and /setprivate together."; |
| return 1; |
| } |
| |
| if (opts.StripPrivate && opts.PdbInPrivate) { |
| errors << "Cannot specify /Qstrip_priv and /Qpdb_in_private together."; |
| return 1; |
| } |
| |
| if ((flagsToInclude & hlsl::options::DriverOption) && |
| opts.InputFile.empty()) { |
| // Input file is required in arguments only for drivers; APIs take this |
| // through an argument. |
| errors << "Required input file argument is missing. use -help to get more " |
| "information."; |
| return 1; |
| } |
| if (opts.OutputHeader.empty() && !opts.VariableName.empty()) { |
| errors |
| << "Cannot specify a header variable name when not writing a header."; |
| return 1; |
| } |
| |
| if (!opts.Preprocess.empty() && |
| (!opts.OutputHeader.empty() || !opts.OutputObject.empty() || |
| !opts.OutputWarnings || !opts.OutputWarningsFile.empty() || |
| !opts.OutputReflectionFile.empty() || !opts.OutputRootSigFile.empty() || |
| !opts.OutputShaderHashFile.empty())) { |
| opts.OutputHeader = ""; |
| opts.OutputObject = ""; |
| opts.OutputWarnings = true; |
| opts.OutputWarningsFile = ""; |
| opts.OutputReflectionFile = ""; |
| opts.OutputRootSigFile = ""; |
| opts.OutputShaderHashFile = ""; |
| errors << "Warning: compiler options ignored with Preprocess."; |
| } |
| |
| if (opts.DumpBin) { |
| if (opts.DisplayIncludeProcess || opts.AstDump || opts.DumpDependencies) { |
| errors << "Cannot perform actions related to sources from a binary file."; |
| return 1; |
| } |
| if (opts.AllResourcesBound || opts.AvoidFlowControl || |
| opts.CodeGenHighLevel || opts.DebugInfo || opts.DefaultColMajor || |
| opts.DefaultRowMajor || opts.Defines.size() != 0 || |
| opts.DisableOptimizations || !opts.EntryPoint.empty() || |
| !opts.ForceRootSigVer.empty() || opts.PreferFlowControl || |
| !opts.TargetProfile.empty()) { |
| errors |
| << "Cannot specify compilation options when reading a binary file."; |
| return 1; |
| } |
| } |
| |
| // XXX TODO: Sort this out, since it's required for new API, but a separate |
| // argument for old APIs. |
| if ((flagsToInclude & hlsl::options::DriverOption) && |
| !(flagsToInclude & hlsl::options::RewriteOption) && |
| opts.TargetProfile.empty() && !opts.DumpBin && opts.Preprocess.empty() && |
| !opts.RecompileFromBinary) { |
| // Target profile is required in arguments only for drivers when compiling; |
| // APIs take this through an argument. |
| errors << "Target profile argument is missing"; |
| return 1; |
| } |
| |
| llvm::StringRef valVersionStr = Args.getLastArgValue(OPT_validator_version); |
| if (!valVersionStr.empty()) { |
| // Parse "major.minor" version string |
| auto verPair = valVersionStr.split("."); |
| llvm::APInt major, minor; |
| if (verPair.first.getAsInteger(0, major) || |
| verPair.second.getAsInteger(0, minor)) { |
| errors << "Format of validator version is \"<major>.<minor>\" (ex: " |
| "\"1.4\")."; |
| return 1; |
| } |
| uint64_t major64 = major.getLimitedValue(); |
| uint64_t minor64 = minor.getLimitedValue(); |
| if (major64 > DXIL::kDxilMajor || |
| (major64 == DXIL::kDxilMajor && minor64 > DXIL::kDxilMinor)) { |
| errors << "Validator version must be less than or equal to current " |
| "internal version."; |
| return 1; |
| } |
| if (major64 == 0 && minor64 != 0) { |
| errors |
| << "If validator major version is 0, minor version must also be 0."; |
| return 1; |
| } |
| opts.ValVerMajor = (unsigned long)major64; |
| opts.ValVerMinor = (unsigned long)minor64; |
| } |
| |
| llvm::StringRef valSelectStr = Args.getLastArgValue(OPT_select_validator); |
| if (!valSelectStr.empty()) { |
| opts.SelectValidator = llvm::StringSwitch<ValidatorSelection>(valSelectStr) |
| .Case("auto", ValidatorSelection::Auto) |
| .Case("internal", ValidatorSelection::Internal) |
| .Case("external", ValidatorSelection::External) |
| .Default(ValidatorSelection::Invalid); |
| if (opts.SelectValidator == ValidatorSelection::Invalid) { |
| errors << "Unsupported value '" << valSelectStr |
| << "for -select-validator option."; |
| return 1; |
| } |
| } |
| |
| if (opts.IsLibraryProfile() && Minor == 0xF) { |
| if (opts.ValVerMajor != UINT_MAX && opts.ValVerMajor != 0) { |
| errors << "Offline library profile cannot be used with non-zero " |
| "-validator-version."; |
| return 1; |
| } |
| // Disable validation for offline link only target |
| opts.DisableValidation = true; |
| |
| // ValVerMajor == 0 means that the module is not meant to ever be validated. |
| opts.ValVerMajor = 0; |
| opts.ValVerMinor = 0; |
| } |
| |
| // These targets are only useful as an intermediate step towards linking to |
| // matching shader targets without going through target downgrading at link |
| // time. Disable lib_6_1 and lib_6_2 if /Vd is not present |
| if (opts.IsLibraryProfile() && (Major < 6 || (Major == 6 && Minor < 3))) { |
| if (!opts.DisableValidation) { |
| errors << "Must disable validation for unsupported lib_6_1 or lib_6_2 " |
| "targets."; |
| return 1; |
| } |
| if (opts.ValVerMajor != UINT_MAX && opts.ValVerMajor != 0) { |
| errors << "non-zero -validator-version cannot be used with library " |
| "profiles lib_6_1 or lib_6_2."; |
| return 1; |
| } |
| |
| // ValVerMajor == 0 means that the module is not meant to ever be validated. |
| opts.ValVerMajor = 0; |
| opts.ValVerMinor = 0; |
| } |
| |
| if (opts.KeepReflectionInDxil && opts.StripReflectionFromDxil) { |
| errors << "-Qstrip_reflect_from_dxil mutually exclusive with " |
| "-Qkeep_reflect_in_dxil."; |
| return 1; |
| } |
| |
| addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, opts.Warnings); |
| |
| // SPIRV Change Starts |
| #ifdef ENABLE_SPIRV_CODEGEN |
| opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false); |
| opts.SpirvOptions.invertY = |
| Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false); |
| opts.SpirvOptions.invertW = |
| Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false); |
| opts.SpirvOptions.supportNonzeroBaseInstance = |
| Args.hasFlag(OPT_fvk_support_nonzero_base_instance, OPT_INVALID, false); |
| opts.SpirvOptions.useGlLayout = |
| Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false); |
| opts.SpirvOptions.useDxLayout = |
| Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false); |
| opts.SpirvOptions.useScalarLayout = |
| Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false); |
| opts.SpirvOptions.useLegacyBufferMatrixOrder = |
| Args.hasFlag(OPT_fspv_use_legacy_buffer_matrix_order, OPT_INVALID, false); |
| opts.SpirvOptions.enableReflect = |
| Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false); |
| opts.SpirvOptions.noWarnIgnoredFeatures = |
| Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false); |
| opts.SpirvOptions.noWarnEmulatedFeatures = |
| Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false); |
| opts.SpirvOptions.flattenResourceArrays = |
| Args.hasFlag(OPT_fspv_flatten_resource_arrays, OPT_INVALID, false); |
| opts.SpirvOptions.reduceLoadSize = |
| Args.hasFlag(OPT_fspv_reduce_load_size, OPT_INVALID, false); |
| opts.SpirvOptions.fixFuncCallArguments = |
| Args.hasFlag(OPT_fspv_fix_func_call_arguments, OPT_INVALID, false); |
| opts.SpirvOptions.autoShiftBindings = |
| Args.hasFlag(OPT_fvk_auto_shift_bindings, OPT_INVALID, false); |
| opts.SpirvOptions.finiteMathOnly = |
| Args.hasFlag(OPT_ffinite_math_only, OPT_fno_finite_math_only, false); |
| opts.SpirvOptions.preserveBindings = |
| Args.hasFlag(OPT_fspv_preserve_bindings, OPT_INVALID, false); |
| opts.SpirvOptions.preserveInterface = |
| Args.hasFlag(OPT_fspv_preserve_interface, OPT_INVALID, false); |
| opts.SpirvOptions.allowRWStructuredBufferArrays = |
| Args.hasFlag(OPT_fvk_allow_rwstructuredbuffer_arrays, OPT_INVALID, false); |
| opts.SpirvOptions.enableMaximalReconvergence = |
| Args.hasFlag(OPT_fspv_enable_maximal_reconvergence, OPT_INVALID, false); |
| |
| if (!handleVkShiftArgs(Args, OPT_fvk_b_shift, "b", &opts.SpirvOptions.bShift, |
| errors) || |
| !handleVkShiftArgs(Args, OPT_fvk_t_shift, "t", &opts.SpirvOptions.tShift, |
| errors) || |
| !handleVkShiftArgs(Args, OPT_fvk_s_shift, "s", &opts.SpirvOptions.sShift, |
| errors) || |
| !handleVkShiftArgs(Args, OPT_fvk_u_shift, "u", &opts.SpirvOptions.uShift, |
| errors)) |
| return 1; |
| |
| opts.SpirvOptions.bindRegister = Args.getAllArgValues(OPT_fvk_bind_register); |
| opts.SpirvOptions.bindGlobals = Args.getAllArgValues(OPT_fvk_bind_globals); |
| opts.SpirvOptions.stageIoOrder = |
| Args.getLastArgValue(OPT_fvk_stage_io_order_EQ, "decl"); |
| if (opts.SpirvOptions.stageIoOrder != "alpha" && |
| opts.SpirvOptions.stageIoOrder != "decl") { |
| errors << "unknown Vulkan stage I/O location assignment order: " |
| << opts.SpirvOptions.stageIoOrder; |
| return 1; |
| } |
| |
| for (const Arg *A : Args.filtered(OPT_fspv_extension_EQ)) { |
| opts.SpirvOptions.allowedExtensions.push_back(A->getValue()); |
| } |
| |
| opts.SpirvOptions.printAll = |
| Args.hasFlag(OPT_fspv_print_all, OPT_INVALID, false); |
| |
| opts.SpirvOptions.debugInfoFile = opts.SpirvOptions.debugInfoSource = false; |
| opts.SpirvOptions.debugInfoLine = opts.SpirvOptions.debugInfoTool = false; |
| opts.SpirvOptions.debugInfoRich = false; |
| opts.SpirvOptions.debugInfoVulkan = false; |
| opts.SpirvOptions.debugSourceLen = kDefaultMaximumSourceLength; |
| if (Args.hasArg(OPT_fspv_debug_EQ)) { |
| opts.DebugInfo = true; |
| for (const Arg *A : Args.filtered(OPT_fspv_debug_EQ)) { |
| const llvm::StringRef v = A->getValue(); |
| if (v == "file") { |
| opts.SpirvOptions.debugInfoFile = true; |
| } else if (v == "source") { |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = true; |
| } else if (v == "line") { |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = true; |
| opts.SpirvOptions.debugInfoLine = true; |
| } else if (v == "tool") { |
| opts.SpirvOptions.debugInfoTool = true; |
| } else if (v == "rich") { |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = false; |
| opts.SpirvOptions.debugInfoLine = true; |
| opts.SpirvOptions.debugInfoRich = true; |
| } else if (v == "rich-with-source") { |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = true; |
| opts.SpirvOptions.debugInfoLine = true; |
| opts.SpirvOptions.debugInfoRich = true; |
| } else if (v == "vulkan") { |
| // For test purposes only |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = false; |
| opts.SpirvOptions.debugInfoLine = true; |
| opts.SpirvOptions.debugInfoRich = true; |
| opts.SpirvOptions.debugInfoVulkan = true; |
| } else if (v == "vulkan-with-source") { |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = true; |
| opts.SpirvOptions.debugInfoLine = true; |
| opts.SpirvOptions.debugInfoRich = true; |
| opts.SpirvOptions.debugInfoVulkan = true; |
| } else if (v == "vulkan-with-source-test") { |
| // For test purposes only |
| opts.SpirvOptions.debugInfoFile = true; |
| opts.SpirvOptions.debugInfoSource = true; |
| opts.SpirvOptions.debugInfoLine = true; |
| opts.SpirvOptions.debugInfoRich = true; |
| opts.SpirvOptions.debugInfoVulkan = true; |
| opts.SpirvOptions.debugSourceLen = kTestingMaximumSourceLength; |
| } else { |
| errors << "unknown SPIR-V debug info control parameter: " << v; |
| return 1; |
| } |
| } |
| } else if (opts.DebugInfo) { |
| // By default turn on all categories |
| opts.SpirvOptions.debugInfoFile = opts.SpirvOptions.debugInfoSource = true; |
| opts.SpirvOptions.debugInfoLine = opts.SpirvOptions.debugInfoTool = true; |
| } |
| |
| opts.SpirvOptions.targetEnv = |
| Args.getLastArgValue(OPT_fspv_target_env_EQ, "vulkan1.0"); |
| |
| // Handle -Oconfig=<comma-separated-list> option. |
| uint32_t numOconfigs = 0; |
| for (const Arg *A : Args.filtered(OPT_Oconfig)) { |
| ++numOconfigs; |
| if (numOconfigs > 1) { |
| errors << "-Oconfig should not be specified more than once"; |
| return 1; |
| } |
| if (Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3)) { |
| errors << "-Oconfig should not be used together with -O"; |
| return 1; |
| } |
| for (const auto v : A->getValues()) { |
| opts.SpirvOptions.optConfig.push_back(v); |
| } |
| } |
| |
| opts.SpirvOptions.entrypointName = |
| Args.getLastArgValue(OPT_fspv_entrypoint_name_EQ); |
| |
| // Check for use of options not implemented in the SPIR-V backend. |
| if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) && |
| hasUnsupportedSpirvOption(Args, errors)) |
| return 1; |
| |
| #else |
| if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_support_nonzero_base_instance, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fspv_use_legacy_buffer_matrix_order, OPT_INVALID, |
| false) || |
| Args.hasFlag(OPT_fspv_flatten_resource_arrays, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fspv_reduce_load_size, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fspv_fix_func_call_arguments, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fspv_print_all, OPT_INVALID, false) || |
| Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) || |
| Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_auto_shift_bindings, OPT_INVALID, false) || |
| Args.hasFlag(OPT_fvk_allow_rwstructuredbuffer_arrays, OPT_INVALID, |
| false) || |
| !Args.getLastArgValue(OPT_fvk_stage_io_order_EQ).empty() || |
| !Args.getLastArgValue(OPT_fspv_debug_EQ).empty() || |
| !Args.getLastArgValue(OPT_fspv_extension_EQ).empty() || |
| !Args.getLastArgValue(OPT_fspv_target_env_EQ).empty() || |
| !Args.getLastArgValue(OPT_Oconfig).empty() || |
| !Args.getLastArgValue(OPT_fvk_bind_register).empty() || |
| !Args.getLastArgValue(OPT_fvk_bind_globals).empty() || |
| !Args.getLastArgValue(OPT_fvk_b_shift).empty() || |
| !Args.getLastArgValue(OPT_fvk_t_shift).empty() || |
| !Args.getLastArgValue(OPT_fvk_s_shift).empty() || |
| !Args.getLastArgValue(OPT_fvk_u_shift).empty()) { |
| errors << "SPIR-V CodeGen not available. " |
| "Please recompile with -DENABLE_SPIRV_CODEGEN=ON."; |
| return 1; |
| } |
| #endif // ENABLE_SPIRV_CODEGEN |
| // SPIRV Change Ends |
| |
| // Validation for DebugInfo here because spirv uses same DebugInfo opt, |
| // and legacy wrappers will add EmbedDebug in this case, leading to this |
| // failing if placed before spirv path sets DebugInfo to true. |
| if (opts.EmbedDebug && !opts.DebugInfo) { |
| errors << "Must enable debug info with /Zi for /Qembed_debug"; |
| return 1; |
| } |
| |
| if (opts.DebugInfo && opts.SourceOnlyDebug) { |
| errors << "Cannot specify both /Zi and /Zs"; |
| return 1; |
| } |
| |
| if (opts.SourceInDebugModule && opts.SourceOnlyDebug) { |
| errors << "Cannot specify both /Qsource_in_debug_module and /Zs"; |
| return 1; |
| } |
| |
| if (opts.DebugInfo && !opts.DebugNameForBinary && !opts.DebugNameForSource) { |
| opts.DebugNameForBinary = true; |
| } else if (opts.DebugNameForBinary && opts.DebugNameForSource) { |
| errors << "Cannot specify both /Zss and /Zsb"; |
| return 1; |
| } |
| |
| if (opts.DebugNameForSource && (!opts.DebugInfo && !opts.SourceOnlyDebug)) { |
| errors << "/Zss requires debug info (/Zi or /Zs)"; |
| return 1; |
| } |
| |
| // Rewriter Options |
| if (flagsToInclude & hlsl::options::RewriteOption) { |
| opts.RWOpt.Unchanged = Args.hasFlag(OPT_rw_unchanged, OPT_INVALID, false); |
| opts.RWOpt.SkipFunctionBody = |
| Args.hasFlag(OPT_rw_skip_function_body, OPT_INVALID, false); |
| opts.RWOpt.SkipStatic = |
| Args.hasFlag(OPT_rw_skip_static, OPT_INVALID, false); |
| opts.RWOpt.GlobalExternByDefault = |
| Args.hasFlag(OPT_rw_global_extern_by_default, OPT_INVALID, false); |
| opts.RWOpt.KeepUserMacro = |
| Args.hasFlag(OPT_rw_keep_user_macro, OPT_INVALID, false); |
| opts.RWOpt.ExtractEntryUniforms = |
| Args.hasFlag(OPT_rw_extract_entry_uniforms, OPT_INVALID, false); |
| opts.RWOpt.RemoveUnusedGlobals = |
| Args.hasFlag(OPT_rw_remove_unused_globals, OPT_INVALID, false); |
| opts.RWOpt.RemoveUnusedFunctions = |
| Args.hasFlag(OPT_rw_remove_unused_functions, OPT_INVALID, false); |
| opts.RWOpt.WithLineDirective = |
| Args.hasFlag(OPT_rw_line_directive, OPT_INVALID, false); |
| opts.RWOpt.DeclGlobalCB = |
| Args.hasFlag(OPT_rw_decl_global_cb, OPT_INVALID, false); |
| if (opts.EntryPoint.empty() && |
| (opts.RWOpt.RemoveUnusedGlobals || opts.RWOpt.ExtractEntryUniforms || |
| opts.RWOpt.RemoveUnusedFunctions)) { |
| errors << "-remove-unused-globals, -remove-unused-functions and " |
| "-extract-entry-uniforms requires entry point (-E) to be " |
| "specified."; |
| return 1; |
| } |
| } |
| |
| opts.Args = std::move(Args); |
| return 0; |
| } |
| |
| /// Sets up the specified DxcDllSupport instance as per the given options. |
| int SetupDxcDllSupport(const DxcOpts &opts, dxc::DxcDllSupport &dxcSupport, |
| llvm::raw_ostream &errors) { |
| if (!opts.ExternalLib.empty()) { |
| DXASSERT(!opts.ExternalFn.empty(), "else ReadDxcOpts should have failed"); |
| HRESULT hrLoad = dxcSupport.InitializeForDll(opts.ExternalLib.data(), |
| opts.ExternalFn.data()); |
| if (DXC_FAILED(hrLoad)) { |
| errors << "Unable to load support for external DLL " << opts.ExternalLib |
| << " with function " << opts.ExternalFn << " - error 0x"; |
| errors.write_hex(hrLoad); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| void CopyArgsToWStrings(const InputArgList &inArgs, unsigned flagsToInclude, |
| std::vector<std::wstring> &outArgs) { |
| ArgStringList stringList; |
| for (const Arg *A : inArgs) { |
| if (A->getOption().hasFlag(flagsToInclude)) { |
| A->renderAsInput(inArgs, stringList); |
| } |
| } |
| for (const char *argText : stringList) { |
| outArgs.emplace_back(Unicode::UTF8ToWideStringOrThrow(argText)); |
| } |
| } |
| |
| SerializeDxilFlags ComputeSerializeDxilFlags(const options::DxcOpts &opts) { |
| SerializeDxilFlags SerializeFlags = SerializeDxilFlags::None; |
| |
| if (opts.EmbedPDBName()) { |
| SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart; |
| } |
| if (opts.EmbedDebugInfo()) { |
| SerializeFlags |= SerializeDxilFlags::IncludeDebugInfoPart; |
| } |
| if (opts.DebugNameForSource) { |
| // Implies name part |
| SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart; |
| SerializeFlags |= SerializeDxilFlags::DebugNameDependOnSource; |
| } else if (opts.DebugNameForBinary) { |
| // Implies name part |
| SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart; |
| } |
| if (!opts.KeepReflectionInDxil) { |
| SerializeFlags |= SerializeDxilFlags::StripReflectionFromDxilPart; |
| } |
| if (!opts.StripReflection) { |
| SerializeFlags |= SerializeDxilFlags::IncludeReflectionPart; |
| } |
| if (opts.StripRootSignature) { |
| SerializeFlags |= SerializeDxilFlags::StripRootSignature; |
| } |
| return SerializeFlags; |
| } |
| |
| } // namespace options |
| } // namespace hlsl |