diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 94a3a4d8cb..73213a6f3a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,7 +45,23 @@ stages: - script: | call utils\hct\hctstart.cmd %HLSL_SRC_DIR% %HLSL_BLD_DIR% call utils\hct\hcttest.cmd -$(configuration) exec - displayName: 'DXIL Execution Tests' + displayName: 'DXIL Execution Tests' + - script: | + call utils\hct\hctstart.cmd %HLSL_SRC_DIR% %HLSL_BLD_DIR% + call utils\hct\hcttest.cmd -$(configuration) compat-suite 1.6 + displayName: 'DXIL Compat Suite Tests (1.6 release)' + condition: succeededOrFailed() + - script: | + call utils\hct\hctstart.cmd %HLSL_SRC_DIR% %HLSL_BLD_DIR% + call utils\hct\hcttest.cmd -$(configuration) compat-suite 1.7 + displayName: 'DXIL Compat Suite Tests (1.7 release)' + condition: succeededOrFailed() + - script: | + call utils\hct\hctstart.cmd %HLSL_SRC_DIR% %HLSL_BLD_DIR% + call utils\hct\hcttest.cmd -$(configuration) compat-suite 1.8 + displayName: 'DXIL Compat Suite Tests (1.8 release)' + condition: succeededOrFailed() + - job: Nix timeoutInMinutes: 165 diff --git a/include/dxc/Support/HLSLOptions.h b/include/dxc/Support/HLSLOptions.h index 4a6868956a..796b41ad05 100644 --- a/include/dxc/Support/HLSLOptions.h +++ b/include/dxc/Support/HLSLOptions.h @@ -227,6 +227,7 @@ class DxcOpts { std::string TimeTrace = ""; // OPT_ftime_trace[EQ] unsigned TimeTraceGranularity = 500; // OPT_ftime_trace_granularity_EQ bool VerifyDiagnostics = false; // OPT_verify + bool Verbose = false; // OPT_verbose // Optimization pass enables, disables and selects OptimizationToggles diff --git a/include/dxc/Support/HLSLOptions.td b/include/dxc/Support/HLSLOptions.td index 4a38e275c3..40182f85b9 100644 --- a/include/dxc/Support/HLSLOptions.td +++ b/include/dxc/Support/HLSLOptions.td @@ -197,6 +197,18 @@ def verify : Joined<["-"], "verify">, Group, Flags<[CoreOption, DriverOption]>, HelpText<"Verify diagnostic output using comment directives">; +def verbose : Flag<["-"], "verbose">, + Group, + Flags<[DriverOption]>, + HelpText<"Allow emission of verbose compiler information">; + +// Short alias '-v' for the verbose option +def v : Flag<["-"], "v">, + Alias, + Group, + Flags<[DriverOption]>, + HelpText<"Alias for -verbose">; + /* def fno_caret_diagnostics : Flag<["-"], "fno-caret-diagnostics">, Group, Flags<[CoreOption]>; diff --git a/include/dxc/Support/dxcapi.extval.h b/include/dxc/Support/dxcapi.extval.h index e479c9cce1..272f840dc7 100644 --- a/include/dxc/Support/dxcapi.extval.h +++ b/include/dxc/Support/dxcapi.extval.h @@ -1,8 +1,12 @@ #include "dxc/Support/dxcapi.use.h" +#include "dxc/WinAdapter.h" +#include "llvm/Support/raw_ostream.h" + #include #include namespace dxc { + class DxcDllExtValidationLoader : public DllLoader { // DxCompilerSupport manages the // lifetime of dxcompiler.dll, while DxilExtValSupport @@ -12,8 +16,8 @@ class DxcDllExtValidationLoader : public DllLoader { std::string DxilDllPath; public: - std::string GetDxilDllPath() { return DxilDllPath; } - bool DxilDllFailedToLoad() { + std::string getDxilDllPath() { return DxilDllPath; } + bool dxilDllFailedToLoad() { return !DxilDllPath.empty() && !DxilExtValSupport.IsEnabled(); } @@ -22,7 +26,7 @@ class DxcDllExtValidationLoader : public DllLoader { HRESULT CreateInstance2Impl(IMalloc *pMalloc, REFCLSID clsid, REFIID riid, IUnknown **pResult) override; - HRESULT Initialize(); + HRESULT initialize(llvm::raw_string_ostream &log); bool IsEnabled() const override { return DxCompilerSupport.IsEnabled(); } }; diff --git a/lib/DxcSupport/HLSLOptions.cpp b/lib/DxcSupport/HLSLOptions.cpp index 06e6a1bed6..57417ade03 100644 --- a/lib/DxcSupport/HLSLOptions.cpp +++ b/lib/DxcSupport/HLSLOptions.cpp @@ -871,6 +871,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude, 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); + opts.Verbose = Args.hasFlag(OPT_verbose, 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)) { diff --git a/lib/DxcSupport/dxcapi.extval.cpp b/lib/DxcSupport/dxcapi.extval.cpp index 699873cc3d..b765928775 100644 --- a/lib/DxcSupport/dxcapi.extval.cpp +++ b/lib/DxcSupport/dxcapi.extval.cpp @@ -1,42 +1,454 @@ +#ifndef _WIN32 +#include "dxc/WinAdapter.h" +#endif + +#include "dxc/Support/Global.h" // for hresult handling +#include "dxc/Support/HLSLOptions.h" #include "dxc/Support/WinIncludes.h" -#include // for hresult handling with DXC_FAILED -#include // C++17 and later +#include // C++17 and later +#include // WinIncludes must come before dxcapi.extval.h +#include "dxc/Support/FileIOHelper.h" #include "dxc/Support/dxcapi.extval.h" +#include "dxc/Support/dxcapi.impl.h" +#include "dxc/Support/microcom.h" +#include "dxc/dxcapi.internal.h" +#include + +#include "llvm/ADT/SmallVector.h" + +namespace { + +// The ExtValidationArgHelper class helps manage arguments for external +// validation, as well as perform the validation step if needed. +class ExtValidationArgHelper { +public: + ExtValidationArgHelper() = default; + ExtValidationArgHelper(const ExtValidationArgHelper &) = delete; + HRESULT initialize(IDxcValidator *NewValidator, LPCWSTR **Arguments, + UINT32 *ArgCount, LPCWSTR TargetProfile = nullptr) { + IFR(setValidator(NewValidator)); + IFR(processArgs(Arguments, ArgCount, TargetProfile)); + return S_OK; + } + + HRESULT doValidation(IDxcOperationResult *CompileResult, REFIID Riid, + void **ValResult) { + + if (!CompileResult) + return E_INVALIDARG; + + // No validation needed; just set the result and return. + if (!NeedsValidation) + return CompileResult->QueryInterface(Riid, ValResult); + + // Get the compiled shader. + CComPtr CompiledBlob; + IFR(CompileResult->GetResult(&CompiledBlob)); + + // If no compiled blob; just place the compile result + // into ValResult and return. + if (!CompiledBlob) + return CompileResult->QueryInterface(Riid, ValResult); + + // Validate the compiled shader. + CComPtr TempValidationResult; + UINT32 DxcValidatorFlags = + DxcValidatorFlags_InPlaceEdit | + (RootSignatureOnly ? DxcValidatorFlags_RootSignatureOnly : 0); + IFR(Validator->Validate(CompiledBlob, DxcValidatorFlags, + &TempValidationResult)); + + // Return the validation result if it failed. + HRESULT HR; + IFR(TempValidationResult->GetStatus(&HR)); + if (FAILED(HR)) { + // prefix the stderr output + fprintf(stderr, "error: validation errors\r\n"); + return TempValidationResult->QueryInterface(Riid, ValResult); + } + + // Validation succeeded. Return the original compile result. + return CompileResult->QueryInterface(Riid, ValResult); + } + +private: + std::vector ArgStorage; + llvm::SmallVector NewArgs; + hlsl::options::DxcOpts Opts; + CComPtr Validator; + UINT32 ValidatorVersionMajor = 0; + UINT32 ValidatorVersionMinor = 0; + bool NeedsValidation = false; + bool RootSignatureOnly = false; + + /// Add argument to ArgStorage to be referenced by NewArgs. + void addArgument(const std::wstring &Arg) { ArgStorage.push_back(Arg); } + + HRESULT setValidator(IDxcValidator *NewValidator) { + DXASSERT(NewValidator, "Invalid Validator argument"); + Validator = NewValidator; + + // 1.0 had no version interface. + ValidatorVersionMajor = 1; + ValidatorVersionMinor = 0; + CComPtr ValidatorVersionInfo; + if (SUCCEEDED( + Validator->QueryInterface(IID_PPV_ARGS(&ValidatorVersionInfo)))) { + if (FAILED(ValidatorVersionInfo->GetVersion(&ValidatorVersionMajor, + &ValidatorVersionMinor))) { + DXASSERT(false, "Failed to get validator version"); + return E_FAIL; + } + } + return S_OK; + } + + /// Process arguments, adding validator version and disable validation if + /// needed. If TargetProfile provided, use this instead of -T argument. + HRESULT processArgs(LPCWSTR **Arguments, UINT32 *ArgCount, + LPCWSTR TargetProfile = nullptr) { + DXASSERT((Arguments && ArgCount) && (*Arguments || *ArgCount == 0) && + *ArgCount < INT_MAX - 3, + "Invalid Arguments and ArgCount arguments"); + NeedsValidation = false; + + // Must not call twice. + DXASSERT(NewArgs.empty(), "Must not call twice"); + + // Parse compiler arguments to Opts. + std::string Errors; + llvm::raw_string_ostream OS(Errors); + hlsl::options::MainArgs MainArgs(static_cast(*ArgCount), *Arguments, + 0); + if (hlsl::options::ReadDxcOpts(hlsl::options::getHlslOptTable(), + hlsl::options::CompilerFlags, MainArgs, Opts, + OS)) + return E_FAIL; + + // Determine important conditions from arguments. + const bool ProduceDxModule = !Opts.AstDump && !Opts.OptDump && +#ifdef ENABLE_SPIRV_CODEGEN + !Opts.GenSPIRV && +#endif + !Opts.DumpDependencies && + !Opts.VerifyDiagnostics && + Opts.Preprocess.empty(); + const bool ProduceFullContainer = ProduceDxModule && !Opts.CodeGenHighLevel; + NeedsValidation = ProduceFullContainer && !Opts.DisableValidation; + + // Check target profile. + if ((TargetProfile && + std::wstring(TargetProfile).compare(0, 8, L"rootsig_") == 0) || + Opts.TargetProfile.startswith("rootsig_")) { + RootSignatureOnly = true; + if (ValidatorVersionMajor == 1 && ValidatorVersionMinor < 5) + NeedsValidation = false; // No rootsig validation before 1.5 + } + + // Add extra arguments as needed + if (ProduceDxModule) { + if (Opts.ValVerMajor == UINT_MAX) { + // If not supplied, add validator version arguments. + addArgument(L"-validator-version"); + std::wstring VerStr = std::to_wstring(ValidatorVersionMajor) + L"." + + std::to_wstring(ValidatorVersionMinor); + addArgument(VerStr); + } + + // Add argument to disable validation, so we can call it ourselves + // later. + if (NeedsValidation) + addArgument(L"-Vd"); + } + + if (ArgStorage.empty()) + return S_OK; + + // Reference added arguments from storage. + NewArgs.reserve(*ArgCount + ArgStorage.size()); + + // Copied arguments refer to original strings. + for (UINT32 I = 0; I < *ArgCount; ++I) + NewArgs.push_back((*Arguments)[I]); + + for (const auto &Arg : ArgStorage) + NewArgs.push_back(Arg.c_str()); + + *Arguments = NewArgs.data(); + *ArgCount = (UINT32)NewArgs.size(); + return S_OK; + } +}; + +// ExternalValidationCompiler wraps a DxCompiler to use an external validator +// instead of the internal one. It disables internal validation by adding +// '-Vd' and sets the default validator version with '-validator-version'. +// After a successful compilation, it uses the provided IDxcValidator to +// perform validation when it would normally be performed. +class ExternalValidationCompiler : public IDxcCompiler2, public IDxcCompiler3 { +public: + ExternalValidationCompiler(IMalloc *Malloc, IDxcValidator *OtherValidator, + REFIID OtherCompilerIID, IUnknown *OtherCompiler) + : Validator(OtherValidator), CompilerIID(OtherCompilerIID), + Compiler(OtherCompiler), m_pMalloc(Malloc) {} + + // IUnknown implementation + DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL() + DXC_MICROCOM_TM_ALLOC(ExternalValidationCompiler) + + // QueryInterface creates a new wrapper for the correct compiler interface + // retrieved by calling QueryInterface on the compiler. The new wrapper + // keeps the IID and returned compiler interface pointer for use in + // associated interface methods. Those methods can then use + // castCompilerUnsafe to upcast the pointer to the appropriate interface + // for the method. + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID Iid, + void **ResultObject) override { + if (!ResultObject) + return E_POINTER; + + *ResultObject = nullptr; + + // Get pointer for an interface the compiler object supports. + CComPtr TempCompiler; + IFR(Compiler->QueryInterface(Iid, (void **)&TempCompiler)); + + try { + // This can throw; do not leak C++ exceptions through COM method + // calls. + CComPtr NewWrapper( + Alloc(m_pMalloc, Validator, Iid, TempCompiler)); + return DoBasicQueryInterface( + NewWrapper.p, Iid, ResultObject); + } catch (...) { + return E_FAIL; + } + } + + // IDxcCompiler implementation + HRESULT STDMETHODCALLTYPE + Compile(IDxcBlob *Source, LPCWSTR SourceName, LPCWSTR EntryPoint, + LPCWSTR TargetProfile, LPCWSTR *Arguments, UINT32 ArgCount, + const DxcDefine *Defines, UINT32 DefineCount, + IDxcIncludeHandler *IncludeHandler, + IDxcOperationResult **ResultObject) override { + if (ResultObject == nullptr) + return E_INVALIDARG; + + DxcThreadMalloc TM(m_pMalloc); + // initialize will update Arguments and ArgCount if needed. + ExtValidationArgHelper Helper; + IFR(Helper.initialize(Validator, &Arguments, &ArgCount, TargetProfile)); + + CComPtr CompileResult; + IFR(castUnsafe()->Compile( + Source, SourceName, EntryPoint, TargetProfile, Arguments, ArgCount, + Defines, DefineCount, IncludeHandler, &CompileResult)); + HRESULT CompileHR; + CompileResult->GetStatus(&CompileHR); + if (SUCCEEDED(CompileHR)) + return Helper.doValidation(CompileResult, IID_PPV_ARGS(ResultObject)); + CompileResult->QueryInterface(ResultObject); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + Preprocess(IDxcBlob *Source, LPCWSTR SourceName, LPCWSTR *Arguments, + UINT32 ArgCount, const DxcDefine *Defines, UINT32 DefineCount, + IDxcIncludeHandler *IncludeHandler, + IDxcOperationResult **ResultObject) override { + return castUnsafe()->Preprocess( + Source, SourceName, Arguments, ArgCount, Defines, DefineCount, + IncludeHandler, ResultObject); + } + + HRESULT STDMETHODCALLTYPE + Disassemble(IDxcBlob *Source, IDxcBlobEncoding **Disassembly) override { + return castUnsafe()->Disassemble(Source, Disassembly); + } + + // IDxcCompiler2 implementation + HRESULT STDMETHODCALLTYPE CompileWithDebug( + IDxcBlob *Source, LPCWSTR SourceName, LPCWSTR EntryPoint, + LPCWSTR TargetProfile, LPCWSTR *Arguments, UINT32 ArgCount, + const DxcDefine *pDefines, UINT32 DefineCount, + IDxcIncludeHandler *IncludeHandler, IDxcOperationResult **ResultObject, + LPWSTR *DebugBlobName, IDxcBlob **DebugBlob) override { + if (ResultObject == nullptr) + return E_INVALIDARG; + + DxcThreadMalloc TM(m_pMalloc); + // ProcessArgs will update Arguments and ArgCount if needed. + ExtValidationArgHelper Helper; + + IFR(Helper.initialize(Validator, &Arguments, &ArgCount, TargetProfile)); + + CComPtr CompileResult; + IFR(castUnsafe()->CompileWithDebug( + Source, SourceName, EntryPoint, TargetProfile, Arguments, ArgCount, + pDefines, DefineCount, IncludeHandler, &CompileResult, DebugBlobName, + DebugBlob)); + + return Helper.doValidation(CompileResult, IID_PPV_ARGS(ResultObject)); + } + + // IDxcCompiler3 implementation + HRESULT STDMETHODCALLTYPE Compile(const DxcBuffer *Source, LPCWSTR *Arguments, + UINT32 ArgCount, + IDxcIncludeHandler *IncludeHandler, + REFIID Riid, + LPVOID *ResultObject) override { + if (ResultObject == nullptr) + return E_INVALIDARG; + + DxcThreadMalloc TM(m_pMalloc); + // ProcessArgs will update Arguments and ArgCount if needed. + ExtValidationArgHelper Helper; + + Helper.initialize(Validator, &Arguments, &ArgCount); + + CComPtr CompileResult; + IFR(castUnsafe()->Compile(Source, Arguments, ArgCount, + IncludeHandler, + IID_PPV_ARGS(&CompileResult))); + + return Helper.doValidation(CompileResult, Riid, ResultObject); + } + + HRESULT STDMETHODCALLTYPE Disassemble(const DxcBuffer *Object, REFIID Riid, + LPVOID *ResultObject) override { + return castUnsafe()->Disassemble(Object, Riid, ResultObject); + } + +private: + CComPtr Validator; + + // This wrapper wraps one particular compiler interface. + // When QueryInterface is called, we create a new wrapper + // for the requested interface, which wraps the result of QueryInterface + // on the compiler object. Compiler pointer is held as IUnknown and must + // be upcast to the appropriate interface on use. + IID CompilerIID; + CComPtr Compiler; + DXC_MICROCOM_TM_REF_FIELDS() + + // Cast current compiler interface pointer. Used from methods of the + // associated interface, assuming that the current compiler interface is + // correct for the method call. + // This will either be casting to the original interface retrieved by + // QueryInterface, or to one from which that interface derives. + template T *castSafe() const { + // Compare stored IID with the IID of T + if (CompilerIID == __uuidof(T)) { + // Safe to cast because the underlying compiler object in + // Compiler originally implemented the interface T + return static_cast(Compiler.p); + } + + return nullptr; + } + + template T *castUnsafe() { + if (T *Safe = castSafe()) + return Safe; + return static_cast(Compiler.p); + } +}; +} // namespace namespace dxc { -HRESULT DxcDllExtValidationLoader::CreateInstanceImpl(REFCLSID clsid, - REFIID riid, - IUnknown **pResult) { - if (DxilExtValSupport.IsEnabled() && clsid == CLSID_DxcValidator) - return DxilExtValSupport.CreateInstance(clsid, riid, pResult); +static HRESULT createCompilerWrapper(IMalloc *Malloc, + SpecificDllLoader &DxCompilerSupport, + SpecificDllLoader &DxilExtValSupport, + REFIID Riid, IUnknown **ResultObject) { + DXASSERT(ResultObject, "Invalid ResultObject"); + *ResultObject = nullptr; - return DxCompilerSupport.CreateInstance(clsid, riid, pResult); + CComPtr Compiler; + CComPtr Validator; + + // Create compiler and validator + if (Malloc) { + IFR(DxCompilerSupport.CreateInstance2(Malloc, CLSID_DxcCompiler, Riid, + &Compiler)); + IFR(DxilExtValSupport.CreateInstance2( + Malloc, CLSID_DxcValidator, &Validator)); + } else { + IFR(DxCompilerSupport.CreateInstance(CLSID_DxcCompiler, Riid, &Compiler)); + IFR(DxilExtValSupport.CreateInstance(CLSID_DxcValidator, + &Validator)); + } + + // Wrap compiler + CComPtr CompilerWrapper = + ExternalValidationCompiler::Alloc(Malloc ? Malloc + : DxcGetThreadMallocNoRef(), + Validator, Riid, Compiler); + return CompilerWrapper->QueryInterface(Riid, (void **)ResultObject); } -HRESULT DxcDllExtValidationLoader::CreateInstance2Impl(IMalloc *pMalloc, - REFCLSID clsid, - REFIID riid, - IUnknown **pResult) { - if (DxilExtValSupport.IsEnabled() && clsid == CLSID_DxcValidator) - return DxilExtValSupport.CreateInstance2(pMalloc, clsid, riid, pResult); +HRESULT DxcDllExtValidationLoader::CreateInstanceImpl(REFCLSID Clsid, + REFIID Riid, + IUnknown **ResultObject) { + if (!ResultObject) + return E_POINTER; - return DxCompilerSupport.CreateInstance2(pMalloc, clsid, riid, pResult); + *ResultObject = nullptr; + + // If there is intent to use an external dxil.dll + if (!DxilDllPath.empty() && !dxilDllFailedToLoad()) { + if (Clsid == CLSID_DxcValidator) { + return DxilExtValSupport.CreateInstance(Clsid, Riid, ResultObject); + } + if (Clsid == CLSID_DxcCompiler) { + return createCompilerWrapper(nullptr, DxCompilerSupport, + DxilExtValSupport, Riid, ResultObject); + } + } + + // Fallback: let DxCompiler handle it + return DxCompilerSupport.CreateInstance(Clsid, Riid, ResultObject); +} + +HRESULT DxcDllExtValidationLoader::CreateInstance2Impl( + IMalloc *Malloc, REFCLSID Clsid, REFIID Riid, IUnknown **ResultObject) { + if (!ResultObject) + return E_POINTER; + + *ResultObject = nullptr; + // If there is intent to use an external dxil.dll + if (!DxilDllPath.empty() && !dxilDllFailedToLoad()) { + if (Clsid == CLSID_DxcValidator) { + return DxilExtValSupport.CreateInstance2(Malloc, Clsid, Riid, + ResultObject); + } + if (Clsid == CLSID_DxcCompiler) { + return createCompilerWrapper(Malloc, DxCompilerSupport, DxilExtValSupport, + Riid, ResultObject); + } + } + + // Fallback: let DxCompiler handle it + return DxCompilerSupport.CreateInstance2(Malloc, Clsid, Riid, ResultObject); } -HRESULT DxcDllExtValidationLoader::Initialize() { +HRESULT +DxcDllExtValidationLoader::initialize(llvm::raw_string_ostream &log) { // Load dxcompiler.dll HRESULT Result = DxCompilerSupport.InitializeForDll(kDxCompilerLib, "DxcCreateInstance"); // if dxcompiler.dll fails to load, return the failed HRESULT - if (DXC_FAILED(Result)) { + if (FAILED(Result)) { + log << "dxcompiler.dll failed to load"; return Result; } // now handle external dxil.dll const char *EnvVarVal = std::getenv("DXC_DXIL_DLL_PATH"); if (!EnvVarVal || std::string(EnvVarVal).empty()) { + // no need to emit anything if external validation isn't wanted return S_OK; } @@ -45,10 +457,19 @@ HRESULT DxcDllExtValidationLoader::Initialize() { // Check if path is absolute and exists if (!DllPath.is_absolute() || !std::filesystem::exists(DllPath)) { + log << "dxil.dll path " << DxilDllPath << " could not be found"; return E_INVALIDARG; } - return DxilExtValSupport.InitializeForDll(DxilDllPath.c_str(), - "DxcCreateInstance"); + log << "Loading external dxil.dll from " << DxilDllPath; + Result = DxilExtValSupport.InitializeForDll(DxilDllPath.c_str(), + "DxcCreateInstance"); + if (FAILED(Result)) { + log << "dxil.dll failed to load"; + return Result; + } + + return Result; } + } // namespace dxc diff --git a/tools/clang/test/CMakeLists.txt b/tools/clang/test/CMakeLists.txt index eb297a75de..704c57c8ea 100644 --- a/tools/clang/test/CMakeLists.txt +++ b/tools/clang/test/CMakeLists.txt @@ -179,10 +179,12 @@ foreach(i RANGE 0 ${loop_end}) set(DXC_PATH ${CMAKE_BINARY_DIR}/tools/clang/test/dxc_releases/${version}/${name}/bin/${TARGET_ARCH}/dxil.dll) # Create a lit target for this release - add_lit_target("check-clang-${name}" "Running clang regression tests with ${name}\n" + add_lit_target("check-${name}" "Running clang regression tests with ${name}\n" ${CMAKE_CURRENT_SOURCE_DIR} - PARAMS ${CLANG_TEST_PARAMS} DXC_DXIL_DLL_PATH=${DXC_PATH} - skip_taef_exec=True + PARAMS ${CLANG_TEST_PARAMS} + DXC_DXIL_DLL_PATH=${DXC_PATH} + DXC_VERSION=${version} + skip_taef_exec=True DEPENDS ${CLANG_TEST_DEPS} ${name} ARGS ${CLANG_TEST_EXTRA_ARGS} ) @@ -192,12 +194,9 @@ foreach(i RANGE 0 ${loop_end}) add_custom_target(check-clang) endif() - # Make check-clang depend on this particular release's tests - add_dependencies(check-clang "check-clang-${name}") - # Hook into check-all if (WIN32 AND TARGET check-all) - add_dependencies(check-all "check-clang-${name}") + add_dependencies(check-all "check-${name}") endif() endif() endforeach() diff --git a/tools/clang/test/CodeGenDXIL/hlsl/types/longvec-wave-intrinsics.hlsl b/tools/clang/test/CodeGenDXIL/hlsl/types/longvec-wave-intrinsics.hlsl index e2aadb0eba..69ee278ac1 100644 --- a/tools/clang/test/CodeGenDXIL/hlsl/types/longvec-wave-intrinsics.hlsl +++ b/tools/clang/test/CodeGenDXIL/hlsl/types/longvec-wave-intrinsics.hlsl @@ -1,3 +1,4 @@ +// REQUIRES: dxil-1-9 // RUN: %dxc -T ps_6_9 %s | FileCheck %s RWByteAddressBuffer buf; diff --git a/tools/clang/test/LitDXILValidation/validate_1_6_2112.hlsl b/tools/clang/test/LitDXILValidation/validate_1_6_2112.hlsl new file mode 100644 index 0000000000..7b711b359f --- /dev/null +++ b/tools/clang/test/LitDXILValidation/validate_1_6_2112.hlsl @@ -0,0 +1,22 @@ +// REQUIRES: v1.6.2112 + +// Verify that the older DLL is being loaded, and that +// due to it being older, it errors on something that would +// be accepted in newer validators. +// Specifically, the v1.6.2112 validator would emit the +// below validation error, while validators newer than 1.6 +// would be able to validate the module. + +// RUN: not dxc -T cs_6_0 -validator-version 1.7 %s 2>&1 | FileCheck %s +// CHECK: error: Validator version in metadata (1.7) is not supported; maximum: (1.6). +// CHECK: error: RWStructuredBuffers may increment or decrement their counters, but not both. +// CHECK: note: at '%3 = call i32 @dx.op.bufferUpdateCounter(i32 70, %dx.types.Handle %1, i8 1)' in block '#0' of function 'main'. +// CHECK: Validation failed. + +RWStructuredBuffer UAV : register(u0); + +[numthreads(64, 1, 1)] +void main(uint tid : SV_GroupIndex) { + UAV[UAV.IncrementCounter()] = tid; + UAV[UAV.DecrementCounter()] = tid * 2; +} diff --git a/tools/clang/test/LitDXILValidation/validate_isspecialfloat.ll b/tools/clang/test/LitDXILValidation/validate_isspecialfloat.ll index 140d16520c..3bf85ac7c7 100644 --- a/tools/clang/test/LitDXILValidation/validate_isspecialfloat.ll +++ b/tools/clang/test/LitDXILValidation/validate_isspecialfloat.ll @@ -1,3 +1,4 @@ +; REQUIRES: dxil-1-8 ; RUN: not %dxv %s 2>&1 | FileCheck %s ; The purpose of this test is to verify an error is emitted when pre-sm6.9 diff --git a/tools/clang/test/SemaHLSL/hlsl/vectors/slice.hlsl b/tools/clang/test/SemaHLSL/hlsl/vectors/slice.hlsl index 529b8ec250..573fb7fae7 100644 --- a/tools/clang/test/SemaHLSL/hlsl/vectors/slice.hlsl +++ b/tools/clang/test/SemaHLSL/hlsl/vectors/slice.hlsl @@ -1,3 +1,4 @@ +// REQUIRES: dxil-1-9 // RUN: %dxc -I %hlsl_headers -T ps_6_9 -E main %s | FileCheck %s #include diff --git a/tools/clang/test/lit.cfg b/tools/clang/test/lit.cfg index 1f3e6dfe3e..3fe96664b3 100644 --- a/tools/clang/test/lit.cfg +++ b/tools/clang/test/lit.cfg @@ -99,6 +99,8 @@ if dxc_dll_path and os.path.exists(dxc_dll_path): lit_config.note("Setting DXC_DXIL_DLL_PATH environment variable") config.environment.update({'DXC_DXIL_DLL_PATH' : dxc_dll_path}) config.available_features.add("dxc_dxil_dll_path") + dxc_version = lit_config.params.get('DXC_VERSION', None) + config.available_features.add(dxc_version) # Tweak the PATH to include the tools dir and the scripts dir. if clang_obj_root is not None: @@ -515,7 +517,10 @@ if config.metal: # Check supported dxil version def get_dxil_version(): - result = subprocess.run([lit.util.which('dxc', llvm_tools_dir), "--version"], stdout=subprocess.PIPE) + # Merge current environment with lit's config.environment + env = os.environ.copy() + env.update(config.environment) + result = subprocess.run([lit.util.which('dxc', llvm_tools_dir), "--version"], stdout=subprocess.PIPE, env=env) output = result.stdout.decode("utf-8") dxcPat = re.compile(r"(dxcompiler.dll|libdxcompiler.so|libdxcompiler.dylib): (?P[0-9]+)\.(?P[0-9]+).") m = dxcPat.search(output) diff --git a/tools/clang/tools/dxclib/dxc.cpp b/tools/clang/tools/dxclib/dxc.cpp index cd29547b9e..ff2d4aa106 100644 --- a/tools/clang/tools/dxclib/dxc.cpp +++ b/tools/clang/tools/dxclib/dxc.cpp @@ -51,6 +51,7 @@ #include "dxc/DxilRootSignature/DxilRootSignature.h" #include "dxc/Support/FileIOHelper.h" #include "dxc/Support/HLSLOptions.h" +#include "dxc/Support/dxcapi.extval.h" #include "dxc/Support/dxcapi.use.h" #include "dxc/Support/microcom.h" #include "dxc/dxcapi.h" @@ -125,7 +126,7 @@ class DxcContext { private: DxcOpts &m_Opts; - SpecificDllLoader &m_dxcSupport; + DxcDllExtValidationLoader &m_dxcSupport; int ActOnBlob(IDxcBlob *pBlob); int ActOnBlob(IDxcBlob *pBlob, IDxcBlob *pDebugBlob, LPCWSTR pDebugBlobName); @@ -155,7 +156,7 @@ class DxcContext { } public: - DxcContext(DxcOpts &Opts, SpecificDllLoader &dxcSupport) + DxcContext(DxcOpts &Opts, DxcDllExtValidationLoader &dxcSupport) : m_Opts(Opts), m_dxcSupport(dxcSupport) {} int Compile(); @@ -167,6 +168,8 @@ class DxcContext { int Link(); void Preprocess(); void GetCompilerVersionInfo(llvm::raw_string_ostream &OS); + int MaybeRunExternalValidatorAndPrintValidationOutput( + const DxcOpts &opts, CComPtr pCompileResult); }; static void WriteBlobToFile(IDxcBlob *pBlob, llvm::StringRef FName, @@ -871,6 +874,12 @@ int DxcContext::Compile() { outputPDBPath += pDebugName.m_pData; } } else { + // This may or may not use the external validator after compilation, + // depending on the environment. Compilation via the Compile(...) + // function is deferred to whatever object was chosen to be pCompiler, + // which must implement the IDxcCompiler interface. External validation + // will only take place if the DXC_DXIL_DLL_PATH env var is set + // correctly. IFT(pCompiler->Compile( pSource, StringRefWide(m_Opts.InputFile), StringRefWide(m_Opts.EntryPoint), StringRefWide(TargetProfile), @@ -1416,7 +1425,6 @@ int dxc::main(int argc, const char **argv_) { const OptTable *optionTable = getHlslOptTable(); MainArgs argStrings(argc, argv_); DxcOpts dxcOpts; - DXCLibraryDllLoader dxcSupport; // Read options and check errors. { @@ -1448,20 +1456,24 @@ int dxc::main(int argc, const char **argv_) { #endif // Setup a helper DLL. + DxcDllExtValidationLoader dxcSupport; { - std::string dllErrorString; - llvm::raw_string_ostream dllErrorStream(dllErrorString); - int dllResult = - SetupSpecificDllLoader(dxcOpts, dxcSupport, dllErrorStream); - dllErrorStream.flush(); - if (dllErrorString.size()) { - fprintf(stderr, "%s\n", dllErrorString.data()); - } - if (dllResult) + std::string dllLogString; + llvm::raw_string_ostream dllErrorStream(dllLogString); + HRESULT dllResult = dxcSupport.initialize(dllErrorStream); + if (DXC_FAILED(dllResult)) { + dllErrorStream << "Unable to load support for external DLL - error 0x"; + dllErrorStream.write_hex(dllResult); + dllErrorStream.flush(); + fprintf(stderr, "%s\n", dllLogString.data()); return dllResult; + } + + // if no errors setting up, print the log string as stdout + if (dxcOpts.Verbose) + fprintf(stdout, "%s\n", dllLogString.data()); } - EnsureEnabled(dxcSupport); DxcContext context(dxcOpts, dxcSupport); // Handle help request, which overrides any other processing. if (dxcOpts.ShowHelp) { diff --git a/tools/clang/tools/dxcompiler/dxcompilerobj.cpp b/tools/clang/tools/dxcompiler/dxcompilerobj.cpp index 84b568df9c..7d6751c361 100644 --- a/tools/clang/tools/dxcompiler/dxcompilerobj.cpp +++ b/tools/clang/tools/dxcompiler/dxcompilerobj.cpp @@ -714,8 +714,6 @@ class DxcCompiler : public IDxcCompiler3, unsigned rootSigMajor = 0; unsigned rootSigMinor = 0; - // NOTE: this calls the validation component from dxil.dll; the built-in - // validator can be used as a fallback. bool produceFullContainer = false; bool needsValidation = false; bool validateRootSigContainer = false; @@ -747,7 +745,7 @@ class DxcCompiler : public IDxcCompiler3, // Parse and apply if (opts.BindingTableDefine.size()) { - // Just pas the define for now because preprocessor is not available + // Just pass the define for now because preprocessor is not available // yet. struct BindingTableParserImpl : public CodeGenOptions::BindingTableParserType { @@ -830,8 +828,6 @@ class DxcCompiler : public IDxcCompiler3, compiler.getLangOpts().HLSLEntryFunction = compiler.getCodeGenOpts().HLSLEntryFunction = ""; - // NOTE: this calls the validation component from dxil.dll; the built-in - // validator can be used as a fallback. produceFullContainer = !opts.CodeGenHighLevel && !opts.AstDump && !opts.OptDump && rootSigMajor == 0 && !opts.DumpDependencies && @@ -930,6 +926,9 @@ class DxcCompiler : public IDxcCompiler3, if (validateRootSigContainer && !opts.DisableValidation) { CComPtr pValErrors; // Validation failure communicated through diagnostic error + // NOTE: this calls the built-in validator by default. An external + // validator can be opted into via the DXC_DXIL_DLL_PATH environment + // variable dxcutil::ValidateRootSignatureInContainer( pOutputBlob, &compiler.getDiagnostics()); } diff --git a/tools/clang/tools/dxcompiler/dxcutil.cpp b/tools/clang/tools/dxcompiler/dxcutil.cpp index 4e5c5c95e8..8add737757 100644 --- a/tools/clang/tools/dxcompiler/dxcutil.cpp +++ b/tools/clang/tools/dxcompiler/dxcutil.cpp @@ -48,7 +48,6 @@ HRESULT RunInternalValidator(IDxcValidator *pValidator, namespace { // AssembleToContainer helper functions. -// return true if the internal validator was used, false otherwise void CreateValidator(CComPtr &pValidator) { IFT(CreateDxcValidator(IID_PPV_ARGS(&pValidator))); } @@ -149,6 +148,8 @@ HRESULT ValidateAndAssembleToContainer(AssembleInputs &inputs) { std::unique_ptr llvmModuleWithDebugInfo; CComPtr pValidator; + + // this uses the internal validator, which is the default. CreateValidator(pValidator); if (llvm::getDebugMetadataVersionFromModule(*inputs.pM) != 0) diff --git a/tools/clang/tools/dxv/dxv.cpp b/tools/clang/tools/dxv/dxv.cpp index 3dedcbfaa5..3c166329f8 100644 --- a/tools/clang/tools/dxv/dxv.cpp +++ b/tools/clang/tools/dxv/dxv.cpp @@ -15,6 +15,7 @@ #include "dxc/DxilContainer/DxilContainer.h" #include "dxc/Support/HLSLOptions.h" +#include "dxc/Support/dxcapi.extval.h" #include "dxc/Support/dxcapi.use.h" #include "dxc/dxcapi.h" #include "dxc/dxcapi.internal.h" @@ -42,10 +43,11 @@ static cl::opt class DxvContext { private: - SpecificDllLoader &m_dxcSupport; + DxcDllExtValidationLoader &m_dxcSupport; public: - DxvContext(SpecificDllLoader &dxcSupport) : m_dxcSupport(dxcSupport) {} + DxvContext(DxcDllExtValidationLoader &dxcSupport) + : m_dxcSupport(dxcSupport) {} void Validate(); }; @@ -160,8 +162,10 @@ int main(int argc, const char **argv) { return 2; } - DxCompilerDllLoader dxcSupport; - IFT(dxcSupport.Initialize()); + DxcDllExtValidationLoader dxcSupport; + std::string dllLogString; + llvm::raw_string_ostream dllErrorStream(dllLogString); + IFT(dxcSupport.initialize(dllErrorStream)); DxvContext context(dxcSupport); pStage = "Validation"; diff --git a/tools/clang/unittests/HLSL/ValidationTest.cpp b/tools/clang/unittests/HLSL/ValidationTest.cpp index 3918901d74..282a4ed61b 100644 --- a/tools/clang/unittests/HLSL/ValidationTest.cpp +++ b/tools/clang/unittests/HLSL/ValidationTest.cpp @@ -4247,24 +4247,26 @@ TEST_F(ValidationTest, UnitTestExtValidationSupport) { SetEnvVarW(L"DXC_DXIL_DLL_PATH", L""); // empty initialization should succeed - VERIFY_SUCCEEDED(ExtSupportEmpty.Initialize()); + std::string dllErrorString; + llvm::raw_string_ostream dllLogStream(dllErrorString); + VERIFY_SUCCEEDED(ExtSupportEmpty.initialize(dllLogStream)); - VERIFY_IS_FALSE(ExtSupportEmpty.DxilDllFailedToLoad()); - std::string EmptyPath = ExtSupportBogus.GetDxilDllPath(); + VERIFY_IS_FALSE(ExtSupportEmpty.dxilDllFailedToLoad()); + std::string EmptyPath = ExtSupportBogus.getDxilDllPath(); VERIFY_ARE_EQUAL_STR(EmptyPath.c_str(), ""); // 2. Test with a bogus path in the environment variable SetEnvVarW(L"DXC_DXIL_DLL_PATH", L"bogus"); if (!ExtSupportBogus.IsEnabled()) { - VERIFY_FAILED(ExtSupportBogus.Initialize()); + VERIFY_FAILED(ExtSupportBogus.initialize(dllLogStream)); } // validate that m_dllExtSupport2 was able to capture the environment // variable's value, and that loading the bogus path was unsuccessful - std::string BogusPath = ExtSupportBogus.GetDxilDllPath(); + std::string BogusPath = ExtSupportBogus.getDxilDllPath(); VERIFY_ARE_EQUAL_STR(BogusPath.c_str(), "bogus"); - VERIFY_IS_TRUE(ExtSupportBogus.DxilDllFailedToLoad()); + VERIFY_IS_TRUE(ExtSupportBogus.dxilDllFailedToLoad()); // 3. Test production of class IDs CLSID_DxcCompiler, CLSID_DxcLinker, // and CLSID_DxcValidator through DxcDllExtValidationSupport. diff --git a/utils/hct/hcttest.cmd b/utils/hct/hcttest.cmd index b9e05ce524..13abe668ec 100644 --- a/utils/hct/hcttest.cmd +++ b/utils/hct/hcttest.cmd @@ -98,6 +98,10 @@ if "%1"=="-clean" ( echo Fallback to taef when use taef only options. set TEST_CLANG_FILTER=%2 shift /1 +) else if "%1"=="compat-suite" ( + set TEST_ALL=0 + set TEST_COMPAT_SUITE=%2 + shift /1 ) else if "%1"=="file-check" ( set TEST_ALL=0 set TEST_MANUAL_FILE_CHECK=1 @@ -330,6 +334,18 @@ if "%TEST_USE_LIT%"=="1" ( cmake --build %HLSL_BLD_DIR% --config %BUILD_CONFIG% --target check-clang set RES_CLANG=!ERRORLEVEL! ) + if "!TEST_COMPAT_SUITE!"=="1.6" ( + cmake --build %HLSL_BLD_DIR% --config %BUILD_CONFIG% --target check-dxc_2021_12_08 + set RES_COMPAT_1_6=!ERRORLEVEL! + ) + if "!TEST_COMPAT_SUITE!"=="1.7" ( + cmake --build %HLSL_BLD_DIR% --config %BUILD_CONFIG% --target check-dxc_2023_08_14 + set RES_COMPAT_1_7=!ERRORLEVEL! + ) + if "!TEST_COMPAT_SUITE!"=="1.8" ( + cmake --build %HLSL_BLD_DIR% --config %BUILD_CONFIG% --target check-dxc_2025_02_20 + set RES_COMPAT_1_8=!ERRORLEVEL! + ) if "!TEST_EXEC!"=="1" ( if defined EXEC_ADAPTER ( py %HLSL_SRC_DIR%/utils/lit/lit.py -v --no-progress-bar --param build_mode=%BUILD_CONFIG% --param clang_site_config=%HLSL_BLD_DIR%/tools/clang/test/lit.site.cfg --param clang_taef_exec_site_config=%HLSL_BLD_DIR%/tools/clang/test/taef_exec/lit.site.cfg %EXEC_ADAPTER% %HLSL_SRC_DIR%/tools/clang/test/taef_exec @@ -423,7 +439,6 @@ if "%TEST_CLANG%"=="1" ( set RES_CLANG=!ERRORLEVEL! ) - if "%TEST_EXEC%"=="1" ( call :copyagility ) @@ -497,6 +512,9 @@ if "%TEST_EXEC%"=="1" ( call :check_result "hcttest-extras tests" %RES_EXTRAS% call :check_result "hcttest-after script" %RES_HCTTEST_AFTER% call :check_result "dxilconv tests" %RES_DXILCONV% +call :check_result "compat-suite 1.6 tests" %RES_COMPAT_1_6% +call :check_result "compat-suite 1.7 tests" %RES_COMPAT_1_7% +call :check_result "compat-suite 1.8 tests" %RES_COMPAT_1_8% set EXIT_CODE=%TESTS_FAILED% if not "%TESTS_PASSED%"=="0" ( @@ -546,7 +564,7 @@ echo clang - run clang tests. echo file-check - run file-check test on single file. echo - hcttest file-check "..\CodeGenHLSL\shader-compat-suite\lib_arg_flatten\lib_arg_flatten.hlsl" echo compat-suite - run compat-suite test. -echo - hcttest compat-suite "..\CodeGenHLSL\shader-compat-suite\lib_arg_flatten" +echo - hcttest compat-suite ^(1.6 ^| 1.7 ^| 1.8^) echo cmd - run command line tool tests. echo dxilconv - run dxilconv tests echo v - run the subset of clang tests that are verified-based.