///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// HLSLOptions.h                                                             //
// Copyright (C) Microsoft Corporation. All rights reserved.                 //
// This file is distributed under the University of Illinois Open Source     //
// License. See LICENSE.TXT for details.                                     //
//                                                                           //
// Support for command-line-style option parsing.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef LLVM_HLSL_OPTIONS_H
#define LLVM_HLSL_OPTIONS_H

#include "dxc/Support/DxcOptToggles.h"
#include "dxc/Support/HLSLVersion.h"
#include "dxc/Support/SPIRVOptions.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/ArgList.h"

#include "dxc/Support/WinIncludes.h"

#include "dxc/dxcapi.h"

#include <map>
#include <set>

namespace llvm {
namespace opt {
class OptTable;
class raw_ostream;
} // namespace opt
} // namespace llvm

namespace dxc {
class DxcDllSupport;
}

namespace hlsl {

enum class SerializeDxilFlags : uint32_t;

namespace options {
/// Flags specifically for clang options.  Must not overlap with
/// llvm::opt::DriverFlag or (for clarity) with clang::driver::options.
enum HlslFlags {
  DriverOption = (1 << 13),
  NoArgumentUnused = (1 << 14),
  CoreOption = (1 << 15),
  ISenseOption = (1 << 16),
  RewriteOption = (1 << 17),
};

enum ID {
  OPT_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
               HELPTEXT, METAVAR)                                              \
  OPT_##ID,
#include "dxc/Support/HLSLOptions.inc"
  LastOption
#undef OPTION
};

const llvm::opt::OptTable *getHlslOptTable();
std::error_code initHlslOptTable();
void cleanupHlslOptTable();

///////////////////////////////////////////////////////////////////////////////
// Helper classes to deal with options.

/// Flags for IDxcCompiler APIs.
static const unsigned CompilerFlags = HlslFlags::CoreOption;
/// Flags for dxc.exe command-line tool.
static const unsigned DxcFlags =
    HlslFlags::CoreOption | HlslFlags::DriverOption;
/// Flags for dxr.exe command-line tool.
static const unsigned DxrFlags =
    HlslFlags::RewriteOption | HlslFlags::DriverOption;
/// Flags for IDxcIntelliSense APIs.
static const unsigned ISenseFlags =
    HlslFlags::CoreOption | HlslFlags::ISenseOption;

/// Use this class to capture preprocessor definitions and manage their
/// lifetime.
class DxcDefines {
public:
  void push_back(llvm::StringRef value);
  LPWSTR DefineValues = nullptr;
  llvm::SmallVector<llvm::StringRef, 8> DefineStrings;
  llvm::SmallVector<DxcDefine, 8> DefineVector;

  ~DxcDefines() { delete[] DefineValues; }
  DxcDefines(const DxcDefines &) = delete;
  DxcDefines() {}
  void BuildDefines(); // Must be called after all defines are pushed back
  UINT32 ComputeNumberOfWCharsNeededForDefines();
  const DxcDefine *data() const { return DefineVector.data(); }
  unsigned size() const { return DefineVector.size(); }
};

struct RewriterOpts {
  bool Unchanged = false;             // OPT_rw_unchanged
  bool SkipFunctionBody = false;      // OPT_rw_skip_function_body
  bool SkipStatic = false;            // OPT_rw_skip_static
  bool GlobalExternByDefault = false; // OPT_rw_global_extern_by_default
  bool KeepUserMacro = false;         // OPT_rw_keep_user_macro
  bool ExtractEntryUniforms = false;  // OPT_rw_extract_entry_uniforms
  bool RemoveUnusedGlobals = false;   // OPT_rw_remove_unused_globals
  bool RemoveUnusedFunctions = false; // OPT_rw_remove_unused_functions
  bool WithLineDirective = false;     // OPT_rw_line_directive
  bool DeclGlobalCB = false;          // OPT_rw_decl_global_cb
};

enum class ValidatorSelection : int {
  Auto,        // Try DXIL.dll; fallback to internal validator
  Internal,    // Force internal validator (even if DXIL.dll is present)
  External,    // Use DXIL.dll, failing compilation if not available
  Invalid = -1 // Invalid
};

/// Use this class to capture all options.
class DxcOpts {
public:
  DxcDefines Defines;
  llvm::opt::InputArgList Args =
      llvm::opt::InputArgList(nullptr, nullptr); // Original arguments.

  llvm::StringRef AssemblyCode;               // OPT_Fc
  llvm::StringRef DebugFile;                  // OPT_Fd
  llvm::StringRef EntryPoint;                 // OPT_entrypoint
  llvm::StringRef ExternalFn;                 // OPT_external_fn
  llvm::StringRef ExternalLib;                // OPT_external_lib
  llvm::StringRef ExtractPrivateFile;         // OPT_getprivate
  llvm::StringRef ForceRootSigVer;            // OPT_force_rootsig_ver
  llvm::StringRef InputFile;                  // OPT_INPUT
  llvm::StringRef OutputHeader;               // OPT_Fh
  llvm::StringRef OutputObject;               // OPT_Fo
  llvm::StringRef OutputWarningsFile;         // OPT_Fe
  llvm::StringRef OutputReflectionFile;       // OPT_Fre
  llvm::StringRef OutputRootSigFile;          // OPT_Frs
  llvm::StringRef OutputShaderHashFile;       // OPT_Fsh
  llvm::StringRef OutputFileForDependencies;  // OPT_write_dependencies_to
  std::string Preprocess;                     // OPT_P
  llvm::StringRef TargetProfile;              // OPT_target_profile
  llvm::StringRef VariableName;               // OPT_Vn
  llvm::StringRef PrivateSource;              // OPT_setprivate
  llvm::StringRef RootSignatureSource;        // OPT_setrootsignature
  llvm::StringRef VerifyRootSignatureSource;  // OPT_verifyrootsignature
  llvm::StringRef RootSignatureDefine;        // OPT_rootsig_define
  llvm::StringRef FloatDenormalMode;          // OPT_denorm
  std::vector<std::string> Exports;           // OPT_exports
  std::vector<std::string> PreciseOutputs;    // OPT_precise_output
  llvm::StringRef DefaultLinkage;             // OPT_default_linkage
  llvm::StringRef ImportBindingTable;         // OPT_import_binding_table
  llvm::StringRef BindingTableDefine;         // OPT_binding_table_define
  llvm::StringRef DiagnosticsFormat;          // OPT_fdiagnostics_format
  unsigned DefaultTextCodePage = DXC_CP_UTF8; // OPT_encoding

  bool AllResourcesBound = false;         // OPT_all_resources_bound
  bool IgnoreOptSemDefs = false;          // OPT_ignore_opt_semdefs
  bool AstDump = false;                   // OPT_ast_dump
  bool AstDumpImplicit = false;           // OPT_ast_dump_implicit
  bool ColorCodeAssembly = false;         // OPT_Cc
  bool CodeGenHighLevel = false;          // OPT_fcgl
  bool AllowPreserveValues = false;       // OPT_preserve_intermediate_values
  bool DebugInfo = false;                 // OPT__SLASH_Zi
  bool DebugNameForBinary = false;        // OPT_Zsb
  bool DebugNameForSource = false;        // OPT_Zss
  bool DumpBin = false;                   // OPT_dumpbin
  bool DumpDependencies = false;          // OPT_dump_dependencies
  bool WriteDependencies = false;         // OPT_write_dependencies
  bool Link = false;                      // OPT_link
  bool WarningAsError = false;            // OPT__SLASH_WX
  bool IEEEStrict = false;                // OPT_Gis
  bool IgnoreLineDirectives = false;      // OPT_ignore_line_directives
  bool DefaultColMajor = false;           // OPT_Zpc
  bool DefaultRowMajor = false;           // OPT_Zpr
  bool DisableValidation = false;         // OPT_VD
  unsigned OptLevel = 0;                  // OPT_O0/O1/O2/O3
  bool DisableOptimizations = false;      // OPT_Od
  bool AvoidFlowControl = false;          // OPT_Gfa
  bool PreferFlowControl = false;         // OPT_Gfp
  bool EnableStrictMode = false;          // OPT_Ges
  bool EnableDX9CompatMode = false;       // OPT_Gec
  bool EnableFXCCompatMode = false;       // internal flag
  LangStd HLSLVersion = LangStd::vUnset;  // OPT_hlsl_version (2015-2021)
  bool Enable16BitTypes = false;          // OPT_enable_16bit_types
  bool OptDump = false;                   // OPT_ODump - dump optimizer commands
  bool OutputWarnings = true;             // OPT_no_warnings
  bool ShowHelp = false;                  // OPT_help
  bool ShowHelpHidden = false;            // OPT__help_hidden
  bool ShowOptionNames = false;           // OPT_fdiagnostics_show_option
  bool ShowVersion = false;               // OPT_version
  bool UseColor = false;                  // OPT_Cc
  bool UseHexLiterals = false;            // OPT_Lx
  bool UseInstructionByteOffsets = false; // OPT_No
  bool UseInstructionNumbers = false;     // OPT_Ni
  bool PackPrefixStable = false;          // OPT_pack_prefix_stable
  bool PackOptimized = false;             // OPT_pack_optimized
  bool DisplayIncludeProcess = false;     // OPT__vi
  bool RecompileFromBinary =
      false; // OPT _Recompile (Recompiling the DXBC binary file not .hlsl file)
  bool StripDebug = false;                   // OPT Qstrip_debug
  bool EmbedDebug = false;                   // OPT Qembed_debug
  bool SourceInDebugModule = false;          // OPT Zs
  bool SourceOnlyDebug = false;              // OPT Qsource_only_debug
  bool PdbInPrivate = false;                 // OPT Qpdb_in_private
  bool StripRootSignature = false;           // OPT_Qstrip_rootsignature
  bool StripPrivate = false;                 // OPT_Qstrip_priv
  bool StripReflection = false;              // OPT_Qstrip_reflect
  bool KeepReflectionInDxil = false;         // OPT_Qkeep_reflect_in_dxil
  bool StripReflectionFromDxil = false;      // OPT_Qstrip_reflect_from_dxil
  bool ExtractRootSignature = false;         // OPT_extractrootsignature
  bool DisassembleColorCoded = false;        // OPT_Cc
  bool DisassembleInstNumbers = false;       // OPT_Ni
  bool DisassembleByteOffset = false;        // OPT_No
  bool DisaseembleHex = false;               // OPT_Lx
  bool LegacyMacroExpansion = false;         // OPT_flegacy_macro_expansion
  bool LegacyResourceReservation = false;    // OPT_flegacy_resource_reservation
  unsigned long AutoBindingSpace = UINT_MAX; // OPT_auto_binding_space
  bool ExportShadersOnly = false;            // OPT_export_shaders_only
  bool ResMayAlias = false;                  // OPT_res_may_alias
  unsigned long ValVerMajor = UINT_MAX,
                ValVerMinor = UINT_MAX; // OPT_validator_version
  ValidatorSelection SelectValidator =
      ValidatorSelection::Auto;         // OPT_select_validator
  unsigned ScanLimit = 0;               // OPT_memdep_block_scan_limit
  bool ForceZeroStoreLifetimes = false; // OPT_force_zero_store_lifetimes
  bool EnableLifetimeMarkers = false;   // OPT_enable_lifetime_markers
  bool ForceDisableLocTracking = false; // OPT_fdisable_loc_tracking
  bool NewInlining = false;             // OPT_fnew_inlining_behavior
  bool TimeReport = false;              // OPT_ftime_report
  std::string TimeTrace = "";           // OPT_ftime_trace[EQ]
  unsigned TimeTraceGranularity = 500;  // OPT_ftime_trace_granularity_EQ
  bool VerifyDiagnostics = false;       // OPT_verify

  // Optimization pass enables, disables and selects
  OptimizationToggles
      OptToggles; // OPT_opt_enable, OPT_opt_disable, OPT_opt_select

  std::set<std::string> IgnoreSemDefs;                // OPT_ignore_semdef
  std::map<std::string, std::string> OverrideSemDefs; // OPT_override_semdef

  bool PrintBeforeAll;                  // OPT_print_before_all
  std::set<std::string> PrintBefore;    // OPT_print_before
  bool PrintAfterAll;                   // OPT_print_after_all
  std::set<std::string> PrintAfter;     // OPT_print_after
  bool EnablePayloadQualifiers = false; // OPT_enable_payload_qualifiers
  bool HandleExceptions = false;        // OPT_disable_exception_handling

  // Rewriter Options
  RewriterOpts RWOpt;

  std::vector<std::string> Warnings;

  bool IsRootSignatureProfile() const;
  bool IsLibraryProfile() const;

  // Helpers to clarify interpretation of flags for behavior in implementation
  bool GenerateFullDebugInfo() const; // Zi
  bool GeneratePDB() const;           // Zi or Zs
  bool EmbedDebugInfo() const;        // Qembed_debug
  bool EmbedPDBName() const;          // Zi or Fd
  bool DebugFileIsDirectory() const;  // Fd ends in '\\'
  llvm::StringRef GetPDBName() const; // Fd name

  // SPIRV Change Starts
#ifdef ENABLE_SPIRV_CODEGEN
  bool GenSPIRV; // OPT_spirv
  clang::spirv::SpirvCodeGenOptions
      SpirvOptions; // All SPIR-V CodeGen-related options
#endif
  // SPIRV Change Ends
};

/// Use this class to capture, convert and handle the lifetime for the
/// command-line arguments to a program.
class MainArgs {
public:
  llvm::SmallVector<std::string, 8> Utf8StringVector;
  llvm::SmallVector<const char *, 8> Utf8CharPtrVector;

  MainArgs() = default;
  MainArgs(int argc, const wchar_t **argv, int skipArgCount = 1);
  MainArgs(int argc, const char **argv, int skipArgCount = 1);
  MainArgs(llvm::ArrayRef<llvm::StringRef> args);
  MainArgs &operator=(const MainArgs &other);
  llvm::ArrayRef<const char *> getArrayRef() const {
    return llvm::ArrayRef<const char *>(Utf8CharPtrVector.data(),
                                        Utf8CharPtrVector.size());
  }
};

/// Use this class to convert a StringRef into a wstring, handling empty values
/// as nulls.
class StringRefWide {
private:
  std::wstring m_value;

public:
  StringRefWide(llvm::StringRef value);
  operator LPCWSTR() const { return m_value.size() ? m_value.data() : nullptr; }
};

/// Reads all options from the given argument strings, populates opts, and
/// validates reporting errors and warnings.
int ReadDxcOpts(const llvm::opt::OptTable *optionTable, unsigned flagsToInclude,
                const MainArgs &argStrings, DxcOpts &opts,
                llvm::raw_ostream &errors);

/// Sets up the specified DxcDllSupport instance as per the given options.
int SetupDxcDllSupport(const DxcOpts &opts, dxc::DxcDllSupport &dxcSupport,
                       llvm::raw_ostream &errors);

void CopyArgsToWStrings(const llvm::opt::InputArgList &inArgs,
                        unsigned flagsToInclude,
                        std::vector<std::wstring> &outArgs);

SerializeDxilFlags ComputeSerializeDxilFlags(const options::DxcOpts &opts);

} // namespace options
} // namespace hlsl

#endif
