From babf3f70c05e6b5b4dd11d27a4c1f8c22dfb8d1b Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Wed, 18 Mar 2026 17:50:00 -0700 Subject: [PATCH 01/10] Add LinAlg execution test infrastructure with Load/Store/Splat tests Introduce LinAlgTests.cpp with a new pattern for execution tests where ShaderOp objects are built programmatically in C++ no ShaderOpArith.xml entries required. Shader source, resources, and root signatures are all defined in the .cpp file. Each test compiles its shader via IDxcCompiler3 to validate the HLSL, then skips GPU dispatch if no SM 6.10 device is available. This ensures shader authoring correctness is always verified. Tests added: - LoadStoreRoundtrip_Wave_F32/I32: MatrixLoadFromDescriptor + MatrixStoreToDescriptor - SplatStore_Wave_F32/I32: FillMatrix + MatrixStoreToDescriptor Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/clang/unittests/HLSLExec/CMakeLists.txt | 1 + .../clang/unittests/HLSLExec/LinAlgTests.cpp | 621 ++++++++++++++++++ 2 files changed, 622 insertions(+) create mode 100644 tools/clang/unittests/HLSLExec/LinAlgTests.cpp diff --git a/tools/clang/unittests/HLSLExec/CMakeLists.txt b/tools/clang/unittests/HLSLExec/CMakeLists.txt index 8282fd5282..5548d0eedc 100644 --- a/tools/clang/unittests/HLSLExec/CMakeLists.txt +++ b/tools/clang/unittests/HLSLExec/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(ExecHLSLTests SHARED ShaderOpTest.cpp TableParameterHandler.cpp LongVectors.cpp + LinAlgTests.cpp HlslExecTestUtils.cpp ExecHLSLTests.rc ) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp new file mode 100644 index 0000000000..4c11460a91 --- /dev/null +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -0,0 +1,621 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// LinAlgTests.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. // +// // +// Execution tests for dx::linalg builtins (Proposal 0035, SM 6.10). // +// // +// Unlike older execution tests, these tests define shader source and // +// resources entirely in C++ — no ShaderOpArith.xml entries required. // +// ShaderOp objects are constructed programmatically and passed to the // +// existing RunShaderOpTestAfterParse infrastructure. // +// // +// Each test always compiles its shader via the DXC API to validate the // +// HLSL, then skips GPU dispatch if no SM 6.10 device is available. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// We need to keep & fix these warnings to integrate smoothly with HLK +#pragma warning(error : 4100 4242 4244 4267 4701 4389 4018) + +#ifndef NOMINMAX +#define NOMINMAX 1 +#endif + +#define INLINE_TEST_METHOD_MARKUP +#include + +#include "ShaderOpTest.h" +#include "dxc/Support/Global.h" +#include "dxc/Support/dxcapi.use.h" + +#include "HlslTestUtils.h" + +#include "HlslExecTestUtils.h" + +#include +#include +#include +#include + +namespace LinAlg { + +// =========================================================================== +// DXIL component type constants +// =========================================================================== +enum ComponentType { + CT_I16 = 2, + CT_U16 = 3, + CT_I32 = 4, + CT_U32 = 5, + CT_I64 = 6, + CT_U64 = 7, + CT_F16 = 8, + CT_F32 = 9, + CT_F64 = 10, +}; + +enum MatrixUse { MU_A = 0, MU_B = 1, MU_Accumulator = 2 }; + +enum MatrixScope { MS_Thread = 0, MS_Wave = 1, MS_ThreadGroup = 2 }; + +enum MatrixLayout { + ML_RowMajor = 0, + ML_ColMajor = 1, + ML_MulOptimal = 2, + ML_OuterProductOptimal = 3, +}; + +// =========================================================================== +// ShaderOp construction helpers +// +// These build st::ShaderOp objects programmatically so that tests are +// fully self-contained in this file — no XML required. +// =========================================================================== + +// Create a ShaderOp for a compute shader dispatch. +static std::unique_ptr +createComputeOp(const char *Source, const char *Target, const char *RootSig, + const char *Args = nullptr, UINT DispatchX = 1, + UINT DispatchY = 1, UINT DispatchZ = 1) { + auto Op = std::make_unique(); + LPCSTR CSName = Op->Strings.insert("CS"); + Op->Name = CSName; + Op->CS = CSName; + Op->RootSignature = Op->Strings.insert(RootSig); + Op->DispatchX = DispatchX; + Op->DispatchY = DispatchY; + Op->DispatchZ = DispatchZ; + Op->UseWarpDevice = true; + + st::ShaderOpShader Shader = {}; + Shader.Name = CSName; + Shader.Target = Op->Strings.insert(Target); + Shader.EntryPoint = Op->Strings.insert("main"); + Shader.Text = Op->Strings.insert(Source); + Shader.Arguments = Args ? Op->Strings.insert(Args) : nullptr; + Shader.Compiled = FALSE; + Shader.Callback = FALSE; + Op->Shaders.push_back(Shader); + + return Op; +} + +// Add a UAV buffer resource to a ShaderOp. +static void addUAVBuffer(st::ShaderOp *Op, const char *Name, UINT64 Width, + bool ReadBack, const char *Init = "zero") { + st::ShaderOpResource Res = {}; + Res.Name = Op->Strings.insert(Name); + Res.Init = Op->Strings.insert(Init); + Res.ReadBack = ReadBack ? TRUE : FALSE; + + Res.HeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + Res.HeapFlags = D3D12_HEAP_FLAG_NONE; + Res.Desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + Res.Desc.Width = Width; + Res.Desc.Height = 1; + Res.Desc.DepthOrArraySize = 1; + Res.Desc.MipLevels = 1; + Res.Desc.SampleDesc.Count = 1; + Res.Desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + Res.Desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + Res.InitialResourceState = D3D12_RESOURCE_STATE_COPY_DEST; + Res.TransitionTo = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + + Op->Resources.push_back(Res); +} + +// Bind a resource to a root UAV parameter by index. +static void addRootUAV(st::ShaderOp *Op, UINT Index, const char *ResName) { + st::ShaderOpRootValue RV = {}; + RV.ResName = Op->Strings.insert(ResName); + RV.HeapName = nullptr; + RV.Index = Index; + Op->RootValues.push_back(RV); +} + +// Run a programmatically-built ShaderOp and return the result. +static std::shared_ptr +runShaderOp(ID3D12Device *Device, dxc::SpecificDllLoader &DxcSupport, + std::unique_ptr Op, + st::ShaderOpTest::TInitCallbackFn InitCallback = nullptr) { + auto OpSet = std::make_shared(); + OpSet->ShaderOps.push_back(std::move(Op)); + + return st::RunShaderOpTestAfterParse(Device, DxcSupport, nullptr, + std::move(InitCallback), + std::move(OpSet)); +} + +// =========================================================================== +// Shader compilation helper +// +// Compiles an HLSL shader using the DXC API to verify it is well-formed. +// This runs without a D3D12 device, so it works even when no SM 6.10 +// hardware is available. Fails the test (via VERIFY) on compile error. +// =========================================================================== + +static void compileShader(dxc::SpecificDllLoader &DxcSupport, + const char *Source, const char *Target, + const std::string &Args) { + CComPtr Compiler; + VERIFY_SUCCEEDED(DxcSupport.CreateInstance(CLSID_DxcCompiler, &Compiler)); + + CComPtr Utils; + VERIFY_SUCCEEDED(DxcSupport.CreateInstance(CLSID_DxcUtils, &Utils)); + + CComPtr SourceBlob; + VERIFY_SUCCEEDED(Utils->CreateBlobFromPinned( + Source, static_cast(strlen(Source)), DXC_CP_UTF8, &SourceBlob)); + + // Build wide-string argument list: -T -E main . + std::vector WArgStorage; + WArgStorage.push_back(L"-T"); + WArgStorage.push_back(std::wstring(Target, Target + strlen(Target))); + WArgStorage.push_back(L"-E"); + WArgStorage.push_back(L"main"); + + // Tokenize the additional arguments string. + std::istringstream SS(Args); + std::string Tok; + while (SS >> Tok) + WArgStorage.push_back(std::wstring(Tok.begin(), Tok.end())); + + std::vector WArgPtrs; + for (const auto &A : WArgStorage) + WArgPtrs.push_back(A.c_str()); + + DxcBuffer Buf = {}; + Buf.Ptr = SourceBlob->GetBufferPointer(); + Buf.Size = SourceBlob->GetBufferSize(); + Buf.Encoding = DXC_CP_UTF8; + + CComPtr Result; + VERIFY_SUCCEEDED(Compiler->Compile(&Buf, WArgPtrs.data(), + static_cast(WArgPtrs.size()), + nullptr, IID_PPV_ARGS(&Result))); + + HRESULT HR; + VERIFY_SUCCEEDED(Result->GetStatus(&HR)); + + if (FAILED(HR)) { + CComPtr Errors; + Result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&Errors), nullptr); + if (Errors && Errors->GetStringLength() > 0) + hlsl_test::LogErrorFmt(L"Shader compilation failed:\n%S", + Errors->GetStringPointer()); + VERIFY_SUCCEEDED(HR); + } +} + +// =========================================================================== +// Compiler arguments builder +// =========================================================================== + +static std::string +buildCompilerArgs(int CompType, int M, int N, int Use, int Scope, int Stride, + int Layout, int NumThreads, bool Enable16Bit = false, + const char *ExtraDefines = nullptr) { + std::stringstream SS; + SS << "-HV 202x"; + SS << " -DCOMP_TYPE=" << CompType; + SS << " -DM_DIM=" << M; + SS << " -DN_DIM=" << N; + SS << " -DUSE=" << Use; + SS << " -DSCOPE=" << Scope; + SS << " -DSTRIDE=" << Stride; + SS << " -DLAYOUT=" << Layout; + SS << " -DNUMTHREADS=" << NumThreads; + if (Enable16Bit) + SS << " -enable-16bit-types"; + if (ExtraDefines) + SS << " " << ExtraDefines; + return SS.str(); +} + +// =========================================================================== +// Verification helpers +// =========================================================================== + +static bool verifyFloatBuffer(const void *Actual, const float *Expected, + size_t Count, bool Verbose, + float Tolerance = 0.0f) { + const float *ActualFloats = static_cast(Actual); + bool Success = true; + for (size_t I = 0; I < Count; I++) { + float Diff = ActualFloats[I] - Expected[I]; + if (Diff < 0) + Diff = -Diff; + if (Diff > Tolerance) { + hlsl_test::LogErrorFmt(L"Mismatch at index %zu: actual=%f, expected=%f", + I, static_cast(ActualFloats[I]), + static_cast(Expected[I])); + Success = false; + } else if (Verbose) { + hlsl_test::LogCommentFmt(L" [%zu] actual=%f, expected=%f (OK)", I, + static_cast(ActualFloats[I]), + static_cast(Expected[I])); + } + } + return Success; +} + +static bool verifyIntBuffer(const void *Actual, const int32_t *Expected, + size_t Count, bool Verbose) { + const int32_t *ActualInts = static_cast(Actual); + bool Success = true; + for (size_t I = 0; I < Count; I++) { + if (ActualInts[I] != Expected[I]) { + hlsl_test::LogErrorFmt(L"Mismatch at index %zu: actual=%d, expected=%d", + I, ActualInts[I], Expected[I]); + Success = false; + } else if (Verbose) { + hlsl_test::LogCommentFmt(L" [%zu] actual=%d, expected=%d (OK)", I, + ActualInts[I], Expected[I]); + } + } + return Success; +} + +// =========================================================================== +// Test parameters +// =========================================================================== + +struct MatrixParams { + int CompType; + int M; + int N; + int Use; + int Scope; + int Layout; + int NumThreads; + bool Enable16Bit; + + int strideBytes() const { + int ElemSize = 4; // default F32/I32 + if (CompType == CT_F16 || CompType == CT_I16 || CompType == CT_U16) + ElemSize = 2; + else if (CompType == CT_F64 || CompType == CT_I64 || CompType == CT_U64) + ElemSize = 8; + if (Layout == ML_RowMajor) + return N * ElemSize; + else + return M * ElemSize; + } + + size_t totalElements() const { return static_cast(M) * N; } + + size_t totalBytes() const { + int ElemSize = 4; + if (CompType == CT_F16 || CompType == CT_I16 || CompType == CT_U16) + ElemSize = 2; + else if (CompType == CT_F64 || CompType == CT_I64 || CompType == CT_U64) + ElemSize = 8; + return totalElements() * ElemSize; + } +}; + +// =========================================================================== +// Test class +// =========================================================================== + +class DxilConf_SM610_LinAlg { +public: + BEGIN_TEST_CLASS(DxilConf_SM610_LinAlg) + TEST_CLASS_PROPERTY( + "Kits.TestName", + "D3D12 - Shader Model 6.10 - LinAlg Matrix Operations") + TEST_CLASS_PROPERTY("Kits.TestId", + "a1b2c3d4-e5f6-7890-abcd-ef1234567890") + TEST_CLASS_PROPERTY( + "Kits.Description", + "Validates SM 6.10 linear algebra matrix operations execute correctly") + TEST_CLASS_PROPERTY( + "Kits.Specification", + "Device.Graphics.D3D12.DXILCore.ShaderModel610.CoreRequirement") + TEST_METHOD_PROPERTY(L"Priority", L"0") + END_TEST_CLASS() + + TEST_CLASS_SETUP(setupClass); + TEST_METHOD_SETUP(setupMethod); + + TEST_METHOD(LoadStoreRoundtrip_Wave_F32); + TEST_METHOD(LoadStoreRoundtrip_Wave_I32); + TEST_METHOD(SplatStore_Wave_F32); + TEST_METHOD(SplatStore_Wave_I32); + +private: + CComPtr D3DDevice; + dxc::SpecificDllLoader DxcSupport; + bool VerboseLogging = false; + bool Initialized = false; + std::optional D3D12SDK; +}; + +// =========================================================================== +// Class setup +// =========================================================================== + +bool DxilConf_SM610_LinAlg::setupClass() { + WEX::TestExecution::SetVerifyOutput VerifySettings( + WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures); + + if (!Initialized) { + Initialized = true; + + // Always initialize DXC compiler — needed for shader compilation checks. + VERIFY_SUCCEEDED( + DxcSupport.InitializeForDll(dxc::kDxCompilerLib, "DxcCreateInstance")); + + D3D12SDK = D3D12SDKSelector(); + + WEX::TestExecution::RuntimeParameters::TryGetValue(L"VerboseLogging", + VerboseLogging); + + bool FailIfRequirementsNotMet = false; +#ifdef _HLK_CONF + FailIfRequirementsNotMet = true; +#endif + WEX::TestExecution::RuntimeParameters::TryGetValue( + L"FailIfRequirementsNotMet", FailIfRequirementsNotMet); + + // Try to create a device. In HLK mode, fail if unavailable. + // In dev mode, D3DDevice stays null and tests will compile shaders + // then skip GPU execution. + if (!D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, + /*SkipUnsupported=*/false)) { + if (FailIfRequirementsNotMet) { + hlsl_test::LogErrorFmt( + L"Device creation failed for SM 6.10, and " + L"FailIfRequirementsNotMet is set."); + return false; + } + // No device — tests will compile shaders and skip execution. + } + } + + return true; +} + +bool DxilConf_SM610_LinAlg::setupMethod() { + // Re-create device if it was lost. If we never had one, that's fine — + // tests compile shaders and skip GPU execution. + if (D3DDevice && D3DDevice->GetDeviceRemovedReason() != S_OK) { + hlsl_test::LogCommentFmt(L"Device was lost!"); + D3DDevice.Release(); + D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, + /*SkipUnsupported=*/false); + } + return true; +} + +// =========================================================================== +// Load/Store roundtrip +// =========================================================================== + +static const char LoadStoreShader[] = R"( + RWByteAddressBuffer Input : register(u0); + RWByteAddressBuffer Output : register(u1); + + [numthreads(NUMTHREADS, 1, 1)] + void main() { + __builtin_LinAlgMatrix + [[__LinAlgMatrix_Attributes(COMP_TYPE, M_DIM, N_DIM, USE, SCOPE)]] + Mat; + __builtin_LinAlg_MatrixLoadFromDescriptor( + Mat, Input, 0, STRIDE, LAYOUT); + __builtin_LinAlg_MatrixStoreToDescriptor( + Mat, Output, 0, STRIDE, LAYOUT); + } +)"; + +static void runLoadStoreRoundtrip(ID3D12Device *Device, + dxc::SpecificDllLoader &DxcSupport, + const MatrixParams &Params, bool Verbose) { + const size_t NumElements = Params.totalElements(); + const size_t BufferSize = Params.totalBytes(); + const int Stride = Params.strideBytes(); + + std::string Args = + buildCompilerArgs(Params.CompType, Params.M, Params.N, Params.Use, + Params.Scope, Stride, Params.Layout, + Params.NumThreads, Params.Enable16Bit); + + // Always verify the shader compiles. + compileShader(DxcSupport, LoadStoreShader, "cs_6_10", Args); + + // Skip GPU execution if no device. + if (!Device) { + hlsl_test::LogCommentFmt( + L"Shader compiled OK; skipping execution (no SM 6.10 device)"); + WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped); + return; + } + + // Build expected data. + std::vector ExpectedFloats(NumElements); + std::vector ExpectedInts(NumElements); + for (size_t I = 0; I < NumElements; I++) { + ExpectedFloats[I] = static_cast(I + 1); + ExpectedInts[I] = static_cast(I + 1); + } + + // Construct the ShaderOp: two UAV buffers, load from one, store to other. + auto Op = createComputeOp(LoadStoreShader, "cs_6_10", "UAV(u0), UAV(u1)", + Args.c_str()); + addUAVBuffer(Op.get(), "Input", BufferSize, false, "byname"); + addUAVBuffer(Op.get(), "Output", BufferSize, true); + addRootUAV(Op.get(), 0, "Input"); + addRootUAV(Op.get(), 1, "Output"); + + auto Result = runShaderOp( + Device, DxcSupport, std::move(Op), + [&](LPCSTR Name, std::vector &Data, st::ShaderOp * /*pOp*/) { + if (_stricmp(Name, "Input") != 0) + return; + if (Params.CompType == CT_F32) { + float *Ptr = reinterpret_cast(Data.data()); + for (size_t I = 0; I < NumElements; I++) + Ptr[I] = static_cast(I + 1); + } else if (Params.CompType == CT_I32) { + int32_t *Ptr = reinterpret_cast(Data.data()); + for (size_t I = 0; I < NumElements; I++) + Ptr[I] = static_cast(I + 1); + } + }); + + MappedData OutData; + Result->Test->GetReadBackData("Output", &OutData); + + if (Params.CompType == CT_F32) { + VERIFY_IS_TRUE(verifyFloatBuffer(OutData.data(), ExpectedFloats.data(), + NumElements, Verbose)); + } else if (Params.CompType == CT_I32) { + VERIFY_IS_TRUE(verifyIntBuffer(OutData.data(), ExpectedInts.data(), + NumElements, Verbose)); + } +} + +void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_F32() { + MatrixParams Params = {}; + Params.CompType = CT_F32; + Params.M = 8; + Params.N = 8; + Params.Use = MU_A; + Params.Scope = MS_Wave; + Params.Layout = ML_RowMajor; + Params.NumThreads = 64; + Params.Enable16Bit = false; + runLoadStoreRoundtrip(D3DDevice, DxcSupport, Params, VerboseLogging); +} + +void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_I32() { + MatrixParams Params = {}; + Params.CompType = CT_I32; + Params.M = 8; + Params.N = 8; + Params.Use = MU_A; + Params.Scope = MS_Wave; + Params.Layout = ML_RowMajor; + Params.NumThreads = 64; + Params.Enable16Bit = false; + runLoadStoreRoundtrip(D3DDevice, DxcSupport, Params, VerboseLogging); +} + +// =========================================================================== +// Splat + Store +// =========================================================================== + +static const char SplatStoreShader[] = R"( + RWByteAddressBuffer Output : register(u0); + + [numthreads(NUMTHREADS, 1, 1)] + void main() { + __builtin_LinAlgMatrix + [[__LinAlgMatrix_Attributes(COMP_TYPE, M_DIM, N_DIM, USE, SCOPE)]] + Mat; + __builtin_LinAlg_FillMatrix(Mat, FILL_VALUE); + __builtin_LinAlg_MatrixStoreToDescriptor( + Mat, Output, 0, STRIDE, LAYOUT); + } +)"; + +static void runSplatStore(ID3D12Device *Device, + dxc::SpecificDllLoader &DxcSupport, + const MatrixParams &Params, float FillValue, + bool Verbose) { + const size_t NumElements = Params.totalElements(); + const size_t BufferSize = Params.totalBytes(); + const int Stride = Params.strideBytes(); + + std::stringstream ExtraDefs; + ExtraDefs << "-DFILL_VALUE=" << FillValue; + + std::string Args = buildCompilerArgs( + Params.CompType, Params.M, Params.N, Params.Use, Params.Scope, Stride, + Params.Layout, Params.NumThreads, Params.Enable16Bit, + ExtraDefs.str().c_str()); + + // Always verify the shader compiles. + compileShader(DxcSupport, SplatStoreShader, "cs_6_10", Args); + + // Skip GPU execution if no device. + if (!Device) { + hlsl_test::LogCommentFmt( + L"Shader compiled OK; skipping execution (no SM 6.10 device)"); + WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped); + return; + } + + std::vector ExpectedFloats(NumElements, FillValue); + std::vector ExpectedInts(NumElements, + static_cast(FillValue)); + + auto Op = + createComputeOp(SplatStoreShader, "cs_6_10", "UAV(u0)", Args.c_str()); + addUAVBuffer(Op.get(), "Output", BufferSize, true); + addRootUAV(Op.get(), 0, "Output"); + + auto Result = runShaderOp(Device, DxcSupport, std::move(Op)); + + MappedData OutData; + Result->Test->GetReadBackData("Output", &OutData); + + if (Params.CompType == CT_F32) { + VERIFY_IS_TRUE(verifyFloatBuffer(OutData.data(), ExpectedFloats.data(), + NumElements, Verbose)); + } else if (Params.CompType == CT_I32) { + VERIFY_IS_TRUE(verifyIntBuffer(OutData.data(), ExpectedInts.data(), + NumElements, Verbose)); + } +} + +void DxilConf_SM610_LinAlg::SplatStore_Wave_F32() { + MatrixParams Params = {}; + Params.CompType = CT_F32; + Params.M = 8; + Params.N = 8; + Params.Use = MU_Accumulator; + Params.Scope = MS_Wave; + Params.Layout = ML_RowMajor; + Params.NumThreads = 64; + Params.Enable16Bit = false; + runSplatStore(D3DDevice, DxcSupport, Params, 42.0f, VerboseLogging); +} + +void DxilConf_SM610_LinAlg::SplatStore_Wave_I32() { + MatrixParams Params = {}; + Params.CompType = CT_I32; + Params.M = 8; + Params.N = 8; + Params.Use = MU_Accumulator; + Params.Scope = MS_Wave; + Params.Layout = ML_RowMajor; + Params.NumThreads = 64; + Params.Enable16Bit = false; + runSplatStore(D3DDevice, DxcSupport, Params, 7.0f, VerboseLogging); +} + +} // namespace LinAlg From d862864d66d71dde5ad79396e373bba6f5bdc95b Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Fri, 20 Mar 2026 20:08:50 -0700 Subject: [PATCH 02/10] Style cleanup and comment improvements - Remove NOMINMAX block (not needed without std::min/max + windows.h) - Remove 'Unlike older execution tests...' paragraph from file header - Convert block comments to /// doc comment style - Remove unhelpful 'Always initialize DXC compiler' comment - Apply clang-format fixes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 4c11460a91..8d267ac54c 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -7,23 +7,11 @@ // // // Execution tests for dx::linalg builtins (Proposal 0035, SM 6.10). // // // -// Unlike older execution tests, these tests define shader source and // -// resources entirely in C++ — no ShaderOpArith.xml entries required. // -// ShaderOp objects are constructed programmatically and passed to the // -// existing RunShaderOpTestAfterParse infrastructure. // -// // -// Each test always compiles its shader via the DXC API to validate the // -// HLSL, then skips GPU dispatch if no SM 6.10 device is available. // -// // /////////////////////////////////////////////////////////////////////////////// // We need to keep & fix these warnings to integrate smoothly with HLK #pragma warning(error : 4100 4242 4244 4267 4701 4389 4018) -#ifndef NOMINMAX -#define NOMINMAX 1 -#endif - #define INLINE_TEST_METHOD_MARKUP #include @@ -70,12 +58,9 @@ enum MatrixLayout { // =========================================================================== // ShaderOp construction helpers -// -// These build st::ShaderOp objects programmatically so that tests are -// fully self-contained in this file — no XML required. // =========================================================================== -// Create a ShaderOp for a compute shader dispatch. +/// Create a ShaderOp for a compute shader dispatch. static std::unique_ptr createComputeOp(const char *Source, const char *Target, const char *RootSig, const char *Args = nullptr, UINT DispatchX = 1, @@ -103,7 +88,7 @@ createComputeOp(const char *Source, const char *Target, const char *RootSig, return Op; } -// Add a UAV buffer resource to a ShaderOp. +/// Add a UAV buffer resource to a ShaderOp. static void addUAVBuffer(st::ShaderOp *Op, const char *Name, UINT64 Width, bool ReadBack, const char *Init = "zero") { st::ShaderOpResource Res = {}; @@ -127,7 +112,7 @@ static void addUAVBuffer(st::ShaderOp *Op, const char *Name, UINT64 Width, Op->Resources.push_back(Res); } -// Bind a resource to a root UAV parameter by index. +/// Bind a resource to a root UAV parameter by index. static void addRootUAV(st::ShaderOp *Op, UINT Index, const char *ResName) { st::ShaderOpRootValue RV = {}; RV.ResName = Op->Strings.insert(ResName); @@ -136,7 +121,7 @@ static void addRootUAV(st::ShaderOp *Op, UINT Index, const char *ResName) { Op->RootValues.push_back(RV); } -// Run a programmatically-built ShaderOp and return the result. +/// Run a programmatically-built ShaderOp and return the result. static std::shared_ptr runShaderOp(ID3D12Device *Device, dxc::SpecificDllLoader &DxcSupport, std::unique_ptr Op, @@ -144,19 +129,18 @@ runShaderOp(ID3D12Device *Device, dxc::SpecificDllLoader &DxcSupport, auto OpSet = std::make_shared(); OpSet->ShaderOps.push_back(std::move(Op)); - return st::RunShaderOpTestAfterParse(Device, DxcSupport, nullptr, - std::move(InitCallback), - std::move(OpSet)); + return st::RunShaderOpTestAfterParse( + Device, DxcSupport, nullptr, std::move(InitCallback), std::move(OpSet)); } // =========================================================================== // Shader compilation helper -// -// Compiles an HLSL shader using the DXC API to verify it is well-formed. -// This runs without a D3D12 device, so it works even when no SM 6.10 -// hardware is available. Fails the test (via VERIFY) on compile error. // =========================================================================== +/// Compiles an HLSL shader using the DXC API to verify it is well-formed. +/// This runs without a D3D12 device, so it works even when no SM 6.10 +/// hardware is available. Fails the test (via VERIFY) on compile error. + static void compileShader(dxc::SpecificDllLoader &DxcSupport, const char *Source, const char *Target, const std::string &Args) { @@ -324,11 +308,9 @@ struct MatrixParams { class DxilConf_SM610_LinAlg { public: BEGIN_TEST_CLASS(DxilConf_SM610_LinAlg) - TEST_CLASS_PROPERTY( - "Kits.TestName", - "D3D12 - Shader Model 6.10 - LinAlg Matrix Operations") - TEST_CLASS_PROPERTY("Kits.TestId", - "a1b2c3d4-e5f6-7890-abcd-ef1234567890") + TEST_CLASS_PROPERTY("Kits.TestName", + "D3D12 - Shader Model 6.10 - LinAlg Matrix Operations") + TEST_CLASS_PROPERTY("Kits.TestId", "a1b2c3d4-e5f6-7890-abcd-ef1234567890") TEST_CLASS_PROPERTY( "Kits.Description", "Validates SM 6.10 linear algebra matrix operations execute correctly") @@ -365,7 +347,6 @@ bool DxilConf_SM610_LinAlg::setupClass() { if (!Initialized) { Initialized = true; - // Always initialize DXC compiler — needed for shader compilation checks. VERIFY_SUCCEEDED( DxcSupport.InitializeForDll(dxc::kDxCompilerLib, "DxcCreateInstance")); From f988d5c77addc66dc0697cc4cc48bf01954e4e97 Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Fri, 20 Mar 2026 20:10:53 -0700 Subject: [PATCH 03/10] Use DXIL enums from DxilConstants.h and refactor MatrixParams - Replace custom ComponentType/MatrixUse/MatrixScope/MatrixLayout enums with hlsl::DXIL types from DxilConstants.h - Add elemSize() helper to eliminate duplicated element size logic in strideBytes() and totalBytes() - Make buildCompilerArgs take const MatrixParams& instead of individual params - Use switch statements for CompType dispatch instead of if-else chains Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 169 +++++++++--------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 8d267ac54c..e4cc916fec 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -16,6 +16,7 @@ #include #include "ShaderOpTest.h" +#include "dxc/DXIL/DxilConstants.h" #include "dxc/Support/Global.h" #include "dxc/Support/dxcapi.use.h" @@ -30,31 +31,26 @@ namespace LinAlg { -// =========================================================================== -// DXIL component type constants -// =========================================================================== -enum ComponentType { - CT_I16 = 2, - CT_U16 = 3, - CT_I32 = 4, - CT_U32 = 5, - CT_I64 = 6, - CT_U64 = 7, - CT_F16 = 8, - CT_F32 = 9, - CT_F64 = 10, -}; - -enum MatrixUse { MU_A = 0, MU_B = 1, MU_Accumulator = 2 }; - -enum MatrixScope { MS_Thread = 0, MS_Wave = 1, MS_ThreadGroup = 2 }; - -enum MatrixLayout { - ML_RowMajor = 0, - ML_ColMajor = 1, - ML_MulOptimal = 2, - ML_OuterProductOptimal = 3, -}; +using hlsl::DXIL::ComponentType; +using hlsl::DXIL::LinalgMatrixLayout; +using hlsl::DXIL::MatrixScope; +using hlsl::DXIL::MatrixUse; + +/// Return the byte size of a single element for the given component type. +static int elemSize(ComponentType CT) { + switch (CT) { + case ComponentType::F16: + case ComponentType::I16: + case ComponentType::U16: + return 2; + case ComponentType::F64: + case ComponentType::I64: + case ComponentType::U64: + return 8; + default: + return 4; + } +} // =========================================================================== // ShaderOp construction helpers @@ -198,21 +194,19 @@ static void compileShader(dxc::SpecificDllLoader &DxcSupport, // Compiler arguments builder // =========================================================================== -static std::string -buildCompilerArgs(int CompType, int M, int N, int Use, int Scope, int Stride, - int Layout, int NumThreads, bool Enable16Bit = false, - const char *ExtraDefines = nullptr) { +static std::string buildCompilerArgs(const MatrixParams &Params, int Stride, + const char *ExtraDefines = nullptr) { std::stringstream SS; SS << "-HV 202x"; - SS << " -DCOMP_TYPE=" << CompType; - SS << " -DM_DIM=" << M; - SS << " -DN_DIM=" << N; - SS << " -DUSE=" << Use; - SS << " -DSCOPE=" << Scope; + SS << " -DCOMP_TYPE=" << static_cast(Params.CompType); + SS << " -DM_DIM=" << Params.M; + SS << " -DN_DIM=" << Params.N; + SS << " -DUSE=" << static_cast(Params.Use); + SS << " -DSCOPE=" << static_cast(Params.Scope); SS << " -DSTRIDE=" << Stride; - SS << " -DLAYOUT=" << Layout; - SS << " -DNUMTHREADS=" << NumThreads; - if (Enable16Bit) + SS << " -DLAYOUT=" << static_cast(Params.Layout); + SS << " -DNUMTHREADS=" << Params.NumThreads; + if (Params.Enable16Bit) SS << " -enable-16bit-types"; if (ExtraDefines) SS << " " << ExtraDefines; @@ -268,37 +262,26 @@ static bool verifyIntBuffer(const void *Actual, const int32_t *Expected, // =========================================================================== struct MatrixParams { - int CompType; + ComponentType CompType; int M; int N; - int Use; - int Scope; - int Layout; + MatrixUse Use; + MatrixScope Scope; + LinalgMatrixLayout Layout; int NumThreads; bool Enable16Bit; int strideBytes() const { - int ElemSize = 4; // default F32/I32 - if (CompType == CT_F16 || CompType == CT_I16 || CompType == CT_U16) - ElemSize = 2; - else if (CompType == CT_F64 || CompType == CT_I64 || CompType == CT_U64) - ElemSize = 8; - if (Layout == ML_RowMajor) - return N * ElemSize; + int ES = elemSize(CompType); + if (Layout == LinalgMatrixLayout::RowMajor) + return N * ES; else - return M * ElemSize; + return M * ES; } size_t totalElements() const { return static_cast(M) * N; } - size_t totalBytes() const { - int ElemSize = 4; - if (CompType == CT_F16 || CompType == CT_I16 || CompType == CT_U16) - ElemSize = 2; - else if (CompType == CT_F64 || CompType == CT_I64 || CompType == CT_U64) - ElemSize = 8; - return totalElements() * ElemSize; - } + size_t totalBytes() const { return totalElements() * elemSize(CompType); } }; // =========================================================================== @@ -419,10 +402,7 @@ static void runLoadStoreRoundtrip(ID3D12Device *Device, const size_t BufferSize = Params.totalBytes(); const int Stride = Params.strideBytes(); - std::string Args = - buildCompilerArgs(Params.CompType, Params.M, Params.N, Params.Use, - Params.Scope, Stride, Params.Layout, - Params.NumThreads, Params.Enable16Bit); + std::string Args = buildCompilerArgs(Params, Stride); // Always verify the shader compiles. compileShader(DxcSupport, LoadStoreShader, "cs_6_10", Args); @@ -456,37 +436,49 @@ static void runLoadStoreRoundtrip(ID3D12Device *Device, [&](LPCSTR Name, std::vector &Data, st::ShaderOp * /*pOp*/) { if (_stricmp(Name, "Input") != 0) return; - if (Params.CompType == CT_F32) { + switch (Params.CompType) { + case ComponentType::F32: { float *Ptr = reinterpret_cast(Data.data()); for (size_t I = 0; I < NumElements; I++) Ptr[I] = static_cast(I + 1); - } else if (Params.CompType == CT_I32) { + break; + } + case ComponentType::I32: { int32_t *Ptr = reinterpret_cast(Data.data()); for (size_t I = 0; I < NumElements; I++) Ptr[I] = static_cast(I + 1); + break; + } + default: + break; } }); MappedData OutData; Result->Test->GetReadBackData("Output", &OutData); - if (Params.CompType == CT_F32) { + switch (Params.CompType) { + case ComponentType::F32: VERIFY_IS_TRUE(verifyFloatBuffer(OutData.data(), ExpectedFloats.data(), NumElements, Verbose)); - } else if (Params.CompType == CT_I32) { + break; + case ComponentType::I32: VERIFY_IS_TRUE(verifyIntBuffer(OutData.data(), ExpectedInts.data(), NumElements, Verbose)); + break; + default: + break; } } void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_F32() { MatrixParams Params = {}; - Params.CompType = CT_F32; + Params.CompType = ComponentType::F32; Params.M = 8; Params.N = 8; - Params.Use = MU_A; - Params.Scope = MS_Wave; - Params.Layout = ML_RowMajor; + Params.Use = MatrixUse::A; + Params.Scope = MatrixScope::Wave; + Params.Layout = LinalgMatrixLayout::RowMajor; Params.NumThreads = 64; Params.Enable16Bit = false; runLoadStoreRoundtrip(D3DDevice, DxcSupport, Params, VerboseLogging); @@ -494,12 +486,12 @@ void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_F32() { void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_I32() { MatrixParams Params = {}; - Params.CompType = CT_I32; + Params.CompType = ComponentType::I32; Params.M = 8; Params.N = 8; - Params.Use = MU_A; - Params.Scope = MS_Wave; - Params.Layout = ML_RowMajor; + Params.Use = MatrixUse::A; + Params.Scope = MatrixScope::Wave; + Params.Layout = LinalgMatrixLayout::RowMajor; Params.NumThreads = 64; Params.Enable16Bit = false; runLoadStoreRoundtrip(D3DDevice, DxcSupport, Params, VerboseLogging); @@ -534,10 +526,8 @@ static void runSplatStore(ID3D12Device *Device, std::stringstream ExtraDefs; ExtraDefs << "-DFILL_VALUE=" << FillValue; - std::string Args = buildCompilerArgs( - Params.CompType, Params.M, Params.N, Params.Use, Params.Scope, Stride, - Params.Layout, Params.NumThreads, Params.Enable16Bit, - ExtraDefs.str().c_str()); + std::string Args = + buildCompilerArgs(Params, Stride, ExtraDefs.str().c_str()); // Always verify the shader compiles. compileShader(DxcSupport, SplatStoreShader, "cs_6_10", Args); @@ -564,23 +554,28 @@ static void runSplatStore(ID3D12Device *Device, MappedData OutData; Result->Test->GetReadBackData("Output", &OutData); - if (Params.CompType == CT_F32) { + switch (Params.CompType) { + case ComponentType::F32: VERIFY_IS_TRUE(verifyFloatBuffer(OutData.data(), ExpectedFloats.data(), NumElements, Verbose)); - } else if (Params.CompType == CT_I32) { + break; + case ComponentType::I32: VERIFY_IS_TRUE(verifyIntBuffer(OutData.data(), ExpectedInts.data(), NumElements, Verbose)); + break; + default: + break; } } void DxilConf_SM610_LinAlg::SplatStore_Wave_F32() { MatrixParams Params = {}; - Params.CompType = CT_F32; + Params.CompType = ComponentType::F32; Params.M = 8; Params.N = 8; - Params.Use = MU_Accumulator; - Params.Scope = MS_Wave; - Params.Layout = ML_RowMajor; + Params.Use = MatrixUse::Accumulator; + Params.Scope = MatrixScope::Wave; + Params.Layout = LinalgMatrixLayout::RowMajor; Params.NumThreads = 64; Params.Enable16Bit = false; runSplatStore(D3DDevice, DxcSupport, Params, 42.0f, VerboseLogging); @@ -588,12 +583,12 @@ void DxilConf_SM610_LinAlg::SplatStore_Wave_F32() { void DxilConf_SM610_LinAlg::SplatStore_Wave_I32() { MatrixParams Params = {}; - Params.CompType = CT_I32; + Params.CompType = ComponentType::I32; Params.M = 8; Params.N = 8; - Params.Use = MU_Accumulator; - Params.Scope = MS_Wave; - Params.Layout = ML_RowMajor; + Params.Use = MatrixUse::Accumulator; + Params.Scope = MatrixScope::Wave; + Params.Layout = LinalgMatrixLayout::RowMajor; Params.NumThreads = 64; Params.Enable16Bit = false; runSplatStore(D3DDevice, DxcSupport, Params, 7.0f, VerboseLogging); From 901d4754ed79640f70faf20f85077a8b9e5b75a7 Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Fri, 20 Mar 2026 20:13:01 -0700 Subject: [PATCH 04/10] Improve setup and device handling - Update setupClass with SkipUnsupported variable and more informative error message following existing HLK test patterns - Update setupMethod with robust device-loss detection and recreation that fails hard (VERIFY) since a working device existed previously - Fix potential UB in runSplatStore by only constructing the expected int vector when CompType is I32 (avoids unconditional float-to-int cast) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 96 +++++++++++-------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index e4cc916fec..0cd203115c 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -190,6 +190,33 @@ static void compileShader(dxc::SpecificDllLoader &DxcSupport, } } +// =========================================================================== +// Test parameters +// =========================================================================== + +struct MatrixParams { + ComponentType CompType; + int M; + int N; + MatrixUse Use; + MatrixScope Scope; + LinalgMatrixLayout Layout; + int NumThreads; + bool Enable16Bit; + + int strideBytes() const { + int ES = elemSize(CompType); + if (Layout == LinalgMatrixLayout::RowMajor) + return N * ES; + else + return M * ES; + } + + size_t totalElements() const { return static_cast(M) * N; } + + size_t totalBytes() const { return totalElements() * elemSize(CompType); } +}; + // =========================================================================== // Compiler arguments builder // =========================================================================== @@ -257,33 +284,6 @@ static bool verifyIntBuffer(const void *Actual, const int32_t *Expected, return Success; } -// =========================================================================== -// Test parameters -// =========================================================================== - -struct MatrixParams { - ComponentType CompType; - int M; - int N; - MatrixUse Use; - MatrixScope Scope; - LinalgMatrixLayout Layout; - int NumThreads; - bool Enable16Bit; - - int strideBytes() const { - int ES = elemSize(CompType); - if (Layout == LinalgMatrixLayout::RowMajor) - return N * ES; - else - return M * ES; - } - - size_t totalElements() const { return static_cast(M) * N; } - - size_t totalBytes() const { return totalElements() * elemSize(CompType); } -}; - // =========================================================================== // Test class // =========================================================================== @@ -345,15 +345,16 @@ bool DxilConf_SM610_LinAlg::setupClass() { WEX::TestExecution::RuntimeParameters::TryGetValue( L"FailIfRequirementsNotMet", FailIfRequirementsNotMet); - // Try to create a device. In HLK mode, fail if unavailable. - // In dev mode, D3DDevice stays null and tests will compile shaders - // then skip GPU execution. + const bool SkipUnsupported = !FailIfRequirementsNotMet; if (!D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, - /*SkipUnsupported=*/false)) { + SkipUnsupported)) { if (FailIfRequirementsNotMet) { hlsl_test::LogErrorFmt( - L"Device creation failed for SM 6.10, and " - L"FailIfRequirementsNotMet is set."); + L"Device creation failed, resulting in test failure, since " + L"FailIfRequirementsNotMet is set. The expectation is that this " + L"test will only be executed if something has previously " + L"determined that the system meets the requirements of this " + L"test."); return false; } // No device — tests will compile shaders and skip execution. @@ -364,14 +365,18 @@ bool DxilConf_SM610_LinAlg::setupClass() { } bool DxilConf_SM610_LinAlg::setupMethod() { - // Re-create device if it was lost. If we never had one, that's fine — - // tests compile shaders and skip GPU execution. + // It's possible a previous test case caused a device removal. If it did we + // need to try and create a new device. if (D3DDevice && D3DDevice->GetDeviceRemovedReason() != S_OK) { - hlsl_test::LogCommentFmt(L"Device was lost!"); + hlsl_test::LogCommentFmt(L"Device was lost! Recreating..."); D3DDevice.Release(); - D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, - /*SkipUnsupported=*/false); + + // We expect recreation to succeed since we had a working device before. + const bool SkipUnsupported = false; + VERIFY_IS_TRUE(D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, + SkipUnsupported)); } + return true; } @@ -540,9 +545,18 @@ static void runSplatStore(ID3D12Device *Device, return; } - std::vector ExpectedFloats(NumElements, FillValue); - std::vector ExpectedInts(NumElements, - static_cast(FillValue)); + std::vector ExpectedFloats; + std::vector ExpectedInts; + switch (Params.CompType) { + case ComponentType::F32: + ExpectedFloats.assign(NumElements, FillValue); + break; + case ComponentType::I32: + ExpectedInts.assign(NumElements, static_cast(FillValue)); + break; + default: + break; + } auto Op = createComputeOp(SplatStoreShader, "cs_6_10", "UAV(u0)", Args.c_str()); From 86d28153ea137010c1ed947f47bf389589c8026a Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Mon, 23 Mar 2026 15:19:46 -0600 Subject: [PATCH 05/10] format --- tools/clang/unittests/HLSLExec/LinAlgTests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 0cd203115c..06338ddcdf 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -531,8 +531,7 @@ static void runSplatStore(ID3D12Device *Device, std::stringstream ExtraDefs; ExtraDefs << "-DFILL_VALUE=" << FillValue; - std::string Args = - buildCompilerArgs(Params, Stride, ExtraDefs.str().c_str()); + std::string Args = buildCompilerArgs(Params, Stride, ExtraDefs.str().c_str()); // Always verify the shader compiles. compileShader(DxcSupport, SplatStoreShader, "cs_6_10", Args); From 00a8237d631e38b0dc2d58a0684d5f0ef6e6705a Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Mon, 23 Mar 2026 16:45:55 -0600 Subject: [PATCH 06/10] cleanup --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 06338ddcdf..2cdea4b5de 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -5,7 +5,7 @@ // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // -// Execution tests for dx::linalg builtins (Proposal 0035, SM 6.10). // +// Execution tests for dx::linalg builtins // // // /////////////////////////////////////////////////////////////////////////////// @@ -52,10 +52,6 @@ static int elemSize(ComponentType CT) { } } -// =========================================================================== -// ShaderOp construction helpers -// =========================================================================== - /// Create a ShaderOp for a compute shader dispatch. static std::unique_ptr createComputeOp(const char *Source, const char *Target, const char *RootSig, @@ -129,14 +125,9 @@ runShaderOp(ID3D12Device *Device, dxc::SpecificDllLoader &DxcSupport, Device, DxcSupport, nullptr, std::move(InitCallback), std::move(OpSet)); } -// =========================================================================== -// Shader compilation helper -// =========================================================================== - /// Compiles an HLSL shader using the DXC API to verify it is well-formed. /// This runs without a D3D12 device, so it works even when no SM 6.10 /// hardware is available. Fails the test (via VERIFY) on compile error. - static void compileShader(dxc::SpecificDllLoader &DxcSupport, const char *Source, const char *Target, const std::string &Args) { @@ -190,10 +181,6 @@ static void compileShader(dxc::SpecificDllLoader &DxcSupport, } } -// =========================================================================== -// Test parameters -// =========================================================================== - struct MatrixParams { ComponentType CompType; int M; @@ -217,10 +204,6 @@ struct MatrixParams { size_t totalBytes() const { return totalElements() * elemSize(CompType); } }; -// =========================================================================== -// Compiler arguments builder -// =========================================================================== - static std::string buildCompilerArgs(const MatrixParams &Params, int Stride, const char *ExtraDefines = nullptr) { std::stringstream SS; @@ -240,10 +223,6 @@ static std::string buildCompilerArgs(const MatrixParams &Params, int Stride, return SS.str(); } -// =========================================================================== -// Verification helpers -// =========================================================================== - static bool verifyFloatBuffer(const void *Actual, const float *Expected, size_t Count, bool Verbose, float Tolerance = 0.0f) { @@ -284,10 +263,6 @@ static bool verifyIntBuffer(const void *Actual, const int32_t *Expected, return Success; } -// =========================================================================== -// Test class -// =========================================================================== - class DxilConf_SM610_LinAlg { public: BEGIN_TEST_CLASS(DxilConf_SM610_LinAlg) @@ -319,10 +294,6 @@ class DxilConf_SM610_LinAlg { std::optional D3D12SDK; }; -// =========================================================================== -// Class setup -// =========================================================================== - bool DxilConf_SM610_LinAlg::setupClass() { WEX::TestExecution::SetVerifyOutput VerifySettings( WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures); @@ -357,7 +328,6 @@ bool DxilConf_SM610_LinAlg::setupClass() { L"test."); return false; } - // No device — tests will compile shaders and skip execution. } } @@ -380,10 +350,6 @@ bool DxilConf_SM610_LinAlg::setupMethod() { return true; } -// =========================================================================== -// Load/Store roundtrip -// =========================================================================== - static const char LoadStoreShader[] = R"( RWByteAddressBuffer Input : register(u0); RWByteAddressBuffer Output : register(u1); @@ -502,10 +468,6 @@ void DxilConf_SM610_LinAlg::LoadStoreRoundtrip_Wave_I32() { runLoadStoreRoundtrip(D3DDevice, DxcSupport, Params, VerboseLogging); } -// =========================================================================== -// Splat + Store -// =========================================================================== - static const char SplatStoreShader[] = R"( RWByteAddressBuffer Output : register(u0); From 7a1b4f461c9588bc52e6b32d286aae9179fc8088 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Mon, 23 Mar 2026 18:43:35 -0600 Subject: [PATCH 07/10] comments --- .../clang/unittests/HLSLExec/LinAlgTests.cpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 2cdea4b5de..7d108ef0de 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -204,7 +204,7 @@ struct MatrixParams { size_t totalBytes() const { return totalElements() * elemSize(CompType); } }; -static std::string buildCompilerArgs(const MatrixParams &Params, int Stride, +static std::string buildCompilerArgs(const MatrixParams &Params, const char *ExtraDefines = nullptr) { std::stringstream SS; SS << "-HV 202x"; @@ -213,7 +213,7 @@ static std::string buildCompilerArgs(const MatrixParams &Params, int Stride, SS << " -DN_DIM=" << Params.N; SS << " -DUSE=" << static_cast(Params.Use); SS << " -DSCOPE=" << static_cast(Params.Scope); - SS << " -DSTRIDE=" << Stride; + SS << " -DSTRIDE=" << Params.strideBytes(); SS << " -DLAYOUT=" << static_cast(Params.Layout); SS << " -DNUMTHREADS=" << Params.NumThreads; if (Params.Enable16Bit) @@ -313,8 +313,6 @@ bool DxilConf_SM610_LinAlg::setupClass() { #ifdef _HLK_CONF FailIfRequirementsNotMet = true; #endif - WEX::TestExecution::RuntimeParameters::TryGetValue( - L"FailIfRequirementsNotMet", FailIfRequirementsNotMet); const bool SkipUnsupported = !FailIfRequirementsNotMet; if (!D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, @@ -338,16 +336,22 @@ bool DxilConf_SM610_LinAlg::setupMethod() { // It's possible a previous test case caused a device removal. If it did we // need to try and create a new device. if (D3DDevice && D3DDevice->GetDeviceRemovedReason() != S_OK) { - hlsl_test::LogCommentFmt(L"Device was lost! Recreating..."); + hlsl_test::LogCommentFmt(L"Device was lost!"); D3DDevice.Release(); + } + + if (!D3DDevice) { + hlsl_test::LogCommentFmt(L"Creating device"); - // We expect recreation to succeed since we had a working device before. + // We expect this to succeed since we had a working device before. + // Fail if it doesn't. const bool SkipUnsupported = false; VERIFY_IS_TRUE(D3D12SDK->createDevice(&D3DDevice, D3D_SHADER_MODEL_6_10, SkipUnsupported)); } return true; + } static const char LoadStoreShader[] = R"( @@ -371,9 +375,8 @@ static void runLoadStoreRoundtrip(ID3D12Device *Device, const MatrixParams &Params, bool Verbose) { const size_t NumElements = Params.totalElements(); const size_t BufferSize = Params.totalBytes(); - const int Stride = Params.strideBytes(); - std::string Args = buildCompilerArgs(Params, Stride); + std::string Args = buildCompilerArgs(Params); // Always verify the shader compiles. compileShader(DxcSupport, LoadStoreShader, "cs_6_10", Args); @@ -421,6 +424,7 @@ static void runLoadStoreRoundtrip(ID3D12Device *Device, break; } default: + DXASSERT(false, "Saw unsupported component type"); break; } }); @@ -438,6 +442,7 @@ static void runLoadStoreRoundtrip(ID3D12Device *Device, NumElements, Verbose)); break; default: + DXASSERT(false, "Saw unsupported component type"); break; } } @@ -488,12 +493,11 @@ static void runSplatStore(ID3D12Device *Device, bool Verbose) { const size_t NumElements = Params.totalElements(); const size_t BufferSize = Params.totalBytes(); - const int Stride = Params.strideBytes(); std::stringstream ExtraDefs; ExtraDefs << "-DFILL_VALUE=" << FillValue; - std::string Args = buildCompilerArgs(Params, Stride, ExtraDefs.str().c_str()); + std::string Args = buildCompilerArgs(Params, ExtraDefs.str().c_str()); // Always verify the shader compiles. compileShader(DxcSupport, SplatStoreShader, "cs_6_10", Args); @@ -516,6 +520,7 @@ static void runSplatStore(ID3D12Device *Device, ExpectedInts.assign(NumElements, static_cast(FillValue)); break; default: + DXASSERT(false, "Saw unsupported component type"); break; } @@ -539,6 +544,7 @@ static void runSplatStore(ID3D12Device *Device, NumElements, Verbose)); break; default: + DXASSERT(false, "Saw unsupported component type"); break; } } From a0e480de5bf56e83cb6bc06a8d449f89417f5737 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Mon, 23 Mar 2026 19:06:58 -0600 Subject: [PATCH 08/10] format --- tools/clang/unittests/HLSLExec/LinAlgTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 7d108ef0de..f56bd46334 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -351,7 +351,6 @@ bool DxilConf_SM610_LinAlg::setupMethod() { } return true; - } static const char LoadStoreShader[] = R"( From 02545074731f6b7060a61f90fe0b97b97ab78821 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Tue, 24 Mar 2026 09:56:39 -0600 Subject: [PATCH 09/10] Address comments --- tools/clang/unittests/HLSLExec/LinAlgTests.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index f56bd46334..9408d97c91 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -300,12 +300,9 @@ bool DxilConf_SM610_LinAlg::setupClass() { if (!Initialized) { Initialized = true; - VERIFY_SUCCEEDED( DxcSupport.InitializeForDll(dxc::kDxCompilerLib, "DxcCreateInstance")); - D3D12SDK = D3D12SDKSelector(); - WEX::TestExecution::RuntimeParameters::TryGetValue(L"VerboseLogging", VerboseLogging); From a576b21635dc09e2bc442d977b10498cd3a51e97 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Tue, 24 Mar 2026 10:46:49 -0600 Subject: [PATCH 10/10] Address comments --- tools/clang/unittests/HLSLExec/LinAlgTests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp index 9408d97c91..b4d56b387e 100644 --- a/tools/clang/unittests/HLSLExec/LinAlgTests.cpp +++ b/tools/clang/unittests/HLSLExec/LinAlgTests.cpp @@ -195,8 +195,7 @@ struct MatrixParams { int ES = elemSize(CompType); if (Layout == LinalgMatrixLayout::RowMajor) return N * ES; - else - return M * ES; + return M * ES; } size_t totalElements() const { return static_cast(M) * N; }