diff --git a/lib/DxilContainer/DxilContainerAssembler.cpp b/lib/DxilContainer/DxilContainerAssembler.cpp index d865971e47..8d4910232f 100644 --- a/lib/DxilContainer/DxilContainerAssembler.cpp +++ b/lib/DxilContainer/DxilContainerAssembler.cpp @@ -14,6 +14,7 @@ #include "dxc/DXIL/DxilEntryProps.h" #include "dxc/DXIL/DxilFunctionProps.h" #include "dxc/DXIL/DxilInstructions.h" +#include "dxc/DXIL/DxilMetadataHelper.h" #include "dxc/DXIL/DxilModule.h" #include "dxc/DXIL/DxilOperations.h" #include "dxc/DXIL/DxilShaderModel.h" @@ -31,6 +32,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Instructions.h" @@ -42,6 +44,9 @@ #include #include // Needed for DxilPipelineStateValidation.h #include +#include +#include +#include using namespace llvm; using namespace hlsl; @@ -679,6 +684,14 @@ unsigned hlsl::LoadViewIDStateFromPSV(unsigned *pOutputData, class DxilPSVWriter : public DxilPartWriter { private: + struct LinAlgMatrixInfo { + DXIL::ComponentType Type = DXIL::ComponentType::Invalid; + uint32_t M = 0; + uint32_t N = 0; + DXIL::MatrixUse Use = DXIL::MatrixUse::A; + DXIL::MatrixScope Scope = DXIL::MatrixScope::Thread; + }; + const DxilModule &m_Module; unsigned m_ValMajor = 0, m_ValMinor = 0; PSVInitInfo m_PSVInitInfo; @@ -690,8 +703,396 @@ class DxilPSVWriter : public DxilPartWriter { std::vector m_SigInputElements; std::vector m_SigOutputElements; std::vector m_SigPatchConstOrPrimElements; + std::vector m_LinAlgShapes; + std::vector m_LinAlgConstructions; + std::vector + m_LinAlgThreadMatrixVectorMultiplies; + std::vector m_LinAlgWaveMatrixMultiplies; + std::vector + m_LinAlgThreadGroupMatrixMultiplies; + std::vector m_LinAlgOuterProducts; + std::vector m_LinAlgAccumulateStores; + std::map m_LinAlgMatrixInfos; unsigned EntryFunctionName = 0; + void LoadLinAlgMatrixInfos() { + NamedMDNode *NMD = m_Module.GetModule()->getNamedMetadata( + DxilMDHelper::kDxilTargetTypesMDName); + if (!NMD) + return; + + for (MDNode *MDN : NMD->operands()) { + MDTuple *MDT = dyn_cast(MDN); + if (!MDT || MDT->getNumOperands() != 6) + continue; + + ConstantAsMetadata *TypeMD = + dyn_cast(MDT->getOperand(0).get()); + if (!TypeMD) + continue; + + unsigned Values[5]; + bool IsValid = true; + for (unsigned I = 0; I < 5; ++I) { + ConstantAsMetadata *ValueMD = + dyn_cast(MDT->getOperand(I + 1).get()); + ConstantInt *Value = + ValueMD ? dyn_cast(ValueMD->getValue()) : nullptr; + if (!Value) { + IsValid = false; + break; + } + Values[I] = + Value->getLimitedValue(std::numeric_limits::max()); + } + if (!IsValid) + continue; + + Type *Ty = TypeMD->getValue()->getType(); + m_LinAlgMatrixInfos.emplace( + Ty, LinAlgMatrixInfo{static_cast(Values[0]), + Values[1], Values[2], + static_cast(Values[3]), + static_cast(Values[4])}); + } + } + + bool GetLinAlgMatrixInfo(Type *Ty, LinAlgMatrixInfo &Info) const { + if (!dxilutil::IsHLSLLinAlgMatrixType(Ty)) + return false; + auto It = m_LinAlgMatrixInfos.find(Ty); + if (It == m_LinAlgMatrixInfos.end()) + return false; + Info = It->second; + return true; + } + + static DXIL::ComponentType GetScalarComponentType(Type *Ty, + bool IsSigned = true) { + if (Ty->isHalfTy()) + return DXIL::ComponentType::F16; + if (Ty->isFloatTy()) + return DXIL::ComponentType::F32; + if (Ty->isDoubleTy()) + return DXIL::ComponentType::F64; + if (!Ty->isIntegerTy()) + return DXIL::ComponentType::Invalid; + switch (Ty->getIntegerBitWidth()) { + case 8: + return IsSigned ? DXIL::ComponentType::I8 : DXIL::ComponentType::U8; + case 16: + return IsSigned ? DXIL::ComponentType::I16 : DXIL::ComponentType::U16; + case 32: + return IsSigned ? DXIL::ComponentType::I32 : DXIL::ComponentType::U32; + case 64: + return IsSigned ? DXIL::ComponentType::I64 : DXIL::ComponentType::U64; + default: + return DXIL::ComponentType::Invalid; + } + } + + static DXIL::ComponentType + GetVectorOrScalarComponentType(Type *Ty, bool IsSigned = true) { + if (VectorType *VT = dyn_cast(Ty)) + return GetScalarComponentType(VT->getElementType(), IsSigned); + return GetScalarComponentType(Ty, IsSigned); + } + + uint32_t AddLinAlgShape(uint32_t M, uint32_t N, uint32_t K) { + for (uint32_t I = 0; I < m_LinAlgShapes.size(); ++I) { + const auto &Shape = m_LinAlgShapes[I]; + if (Shape.M == M && Shape.N == N && Shape.K == K) + return I; + } + m_LinAlgShapes.push_back({M, N, K}); + return static_cast(m_LinAlgShapes.size() - 1); + } + + uint32_t AddShapeIndexArray(ArrayRef ShapeIndexes) { + for (uint32_t Offset = 0; + Offset + ShapeIndexes.size() <= m_SemanticIndexBuffer.size(); + ++Offset) { + if (std::equal(ShapeIndexes.begin(), ShapeIndexes.end(), + m_SemanticIndexBuffer.begin() + Offset)) + return Offset; + } + uint32_t Offset = static_cast(m_SemanticIndexBuffer.size()); + m_SemanticIndexBuffer.append(ShapeIndexes.begin(), ShapeIndexes.end()); + return Offset; + } + + static void AddUniqueIndex(std::vector &Indexes, uint32_t Index) { + if (std::find(Indexes.begin(), Indexes.end(), Index) == Indexes.end()) + Indexes.push_back(Index); + } + + uint8_t GetMatVecLayoutFlags(Value *Matrix) const { + SmallVector Worklist(1, Matrix); + SmallPtrSet Visited; + uint8_t Flags = 0; + while (!Worklist.empty()) { + Value *V = Worklist.pop_back_val(); + if (!Visited.insert(V).second) + continue; + + if (PHINode *Phi = dyn_cast(V)) { + Worklist.append(Phi->incoming_values().begin(), + Phi->incoming_values().end()); + continue; + } + + if (SelectInst *Select = dyn_cast(V)) { + Worklist.push_back(Select->getTrueValue()); + Worklist.push_back(Select->getFalseValue()); + continue; + } + + if (CallInst *CI = dyn_cast(V)) { + DXIL::OpCode OpCode = OP::getOpCode(CI); + if (OpCode == DXIL::OpCode::LinAlgCopyConvertMatrix) { + DxilInst_LinAlgCopyConvertMatrix Op(CI); + Worklist.push_back(Op.get_srcMatrix()); + continue; + } + + if (OpCode == DXIL::OpCode::LinAlgMatrixLoadFromDescriptor) { + DxilInst_LinAlgMatrixLoadFromDescriptor Op(CI); + auto *Layout = dyn_cast(Op.get_layout()); + if (!Layout) + continue; + + DXIL::MatrixLayout LayoutValue = + static_cast(Layout->getZExtValue()); + if (LayoutValue == DXIL::MatrixLayout::MulOptimalTranspose) + Flags |= static_cast( + PSVLinAlgThreadMatrixVectorMultiplyFlag::MatrixTransposed); + else if (LayoutValue != DXIL::MatrixLayout::MulOptimal) + Flags |= + static_cast(PSVLinAlgThreadMatrixVectorMultiplyFlag:: + MatrixNonMulOptimalLayout); + } + } + } + return Flags; + } + + void CollectLinAlgRuntimeInfo() { + using ConstructionKey = std::pair; + using MultiplyKey = std::tuple; + std::map> ConstructionShapes; + std::map> MultiplyShapes; + + auto CollectConstruction = [&](Type *Ty) { + LinAlgMatrixInfo Matrix; + if (!GetLinAlgMatrixInfo(Ty, Matrix) || + Matrix.Scope == DXIL::MatrixScope::Thread) + return; + uint32_t M = Matrix.Use == DXIL::MatrixUse::B ? 0 : Matrix.M; + uint32_t N = Matrix.Use == DXIL::MatrixUse::A ? 0 : Matrix.N; + uint32_t K = + Matrix.Use == DXIL::MatrixUse::Accumulator + ? 0 + : (Matrix.Use == DXIL::MatrixUse::A ? Matrix.N : Matrix.M); + uint32_t Shape = AddLinAlgShape(M, N, K); + AddUniqueIndex(ConstructionShapes[{static_cast(Matrix.Type), + static_cast(Matrix.Use)}], + Shape); + }; + + for (const Function &F : m_Module.GetModule()->functions()) { + for (const BasicBlock &BB : F) { + for (const Instruction &I : BB) { + const CallInst *ConstCI = dyn_cast(&I); + if (!ConstCI || !OP::IsDxilOpFuncCallInst(ConstCI)) + continue; + + CallInst *CI = const_cast(ConstCI); + DXIL::OpCode OpCode = OP::getOpCode(CI); + bool IsSpecializedUse = true; + switch (OpCode) { + case DXIL::OpCode::LinAlgMatVecMul: + case DXIL::OpCode::LinAlgMatVecMulAdd: { + DxilInst_LinAlgMatVecMul Op(CI); + LinAlgMatrixInfo Matrix; + if (!GetLinAlgMatrixInfo(Op.get_matrix()->getType(), Matrix)) + break; + bool IsSigned = + cast(Op.get_isOutputSigned())->getZExtValue() != 0; + DXIL::ComponentType ResultType = + GetVectorOrScalarComponentType(CI->getType(), IsSigned); + DXIL::ComponentType InputType = static_cast( + cast(Op.get_interpretation())->getZExtValue()); + uint8_t Flags = GetMatVecLayoutFlags(Op.get_matrix()); + auto It = std::find_if( + m_LinAlgThreadMatrixVectorMultiplies.begin(), + m_LinAlgThreadMatrixVectorMultiplies.end(), + [&](const PSVLinAlgThreadMatrixVectorMultiply0 &Record) { + return Record.ResultType == + static_cast(ResultType) && + Record.MatrixType == + static_cast(Matrix.Type) && + Record.VectorInputType == + static_cast(InputType); + }); + if (It == m_LinAlgThreadMatrixVectorMultiplies.end()) + m_LinAlgThreadMatrixVectorMultiplies.push_back( + {static_cast(ResultType), + static_cast(Matrix.Type), + static_cast(InputType), Flags}); + else + It->Flags |= Flags; + break; + } + case DXIL::OpCode::LinAlgMatrixMultiply: + case DXIL::OpCode::LinAlgMatrixMultiplyAccumulate: { + DxilInst_LinAlgMatrixMultiply Op(CI); + LinAlgMatrixInfo Result, A, B; + if (!GetLinAlgMatrixInfo(CI->getType(), Result) || + !GetLinAlgMatrixInfo(Op.get_matrixA()->getType(), A) || + !GetLinAlgMatrixInfo(Op.get_matrixB()->getType(), B)) + break; + uint32_t Shape = AddLinAlgShape(A.M, B.N, A.N); + AddUniqueIndex(MultiplyShapes[{static_cast(Result.Scope), + static_cast(Result.Type), + static_cast(A.Type), + static_cast(B.Type)}], + Shape); + break; + } + case DXIL::OpCode::LinAlgMatrixOuterProduct: { + DxilInst_LinAlgMatrixOuterProduct Op(CI); + LinAlgMatrixInfo Result; + if (!GetLinAlgMatrixInfo(CI->getType(), Result)) + break; + PSVLinAlgOuterProduct0 Record = { + static_cast(Result.Type), + static_cast(GetVectorOrScalarComponentType( + Op.get_vectorA()->getType())), + {0, 0}}; + if (std::find_if( + m_LinAlgOuterProducts.begin(), m_LinAlgOuterProducts.end(), + [&](const PSVLinAlgOuterProduct0 &Existing) { + return Existing.ResultType == Record.ResultType && + Existing.VectorInputType == Record.VectorInputType; + }) == m_LinAlgOuterProducts.end()) + m_LinAlgOuterProducts.push_back(Record); + break; + } + case DXIL::OpCode::LinAlgMatrixAccumulateToDescriptor: + case DXIL::OpCode::LinAlgMatrixAccumulateToMemory: { + Value *MatrixValue; + uint8_t Flag; + if (OpCode == DXIL::OpCode::LinAlgMatrixAccumulateToDescriptor) { + DxilInst_LinAlgMatrixAccumulateToDescriptor Op(CI); + Flag = + static_cast(PSVLinAlgAccumulateStoreFlag::RawBuffer); + MatrixValue = Op.get_matrix(); + } else { + DxilInst_LinAlgMatrixAccumulateToMemory Op(CI); + Flag = static_cast( + PSVLinAlgAccumulateStoreFlag::GroupShared); + MatrixValue = Op.get_matrix(); + } + + LinAlgMatrixInfo Matrix; + if (!GetLinAlgMatrixInfo(MatrixValue->getType(), Matrix)) + break; + DXIL::ComponentType AccumulatorType = Matrix.Type; + auto It = std::find_if( + m_LinAlgAccumulateStores.begin(), + m_LinAlgAccumulateStores.end(), + [AccumulatorType](const PSVLinAlgAccumulateStore0 &Record) { + return Record.AccumulatorType == + static_cast(AccumulatorType); + }); + if (It == m_LinAlgAccumulateStores.end()) + m_LinAlgAccumulateStores.push_back( + {static_cast(AccumulatorType), Flag, {0, 0}}); + else + It->Flags |= Flag; + break; + } + case DXIL::OpCode::LinAlgVectorAccumulateToDescriptor: { + DxilInst_LinAlgVectorAccumulateToDescriptor Op(CI); + DXIL::ComponentType Type = + GetVectorOrScalarComponentType(Op.get_vector()->getType()); + auto It = std::find_if( + m_LinAlgAccumulateStores.begin(), + m_LinAlgAccumulateStores.end(), + [&](const PSVLinAlgAccumulateStore0 &Record) { + return Record.AccumulatorType == static_cast(Type); + }); + if (It == m_LinAlgAccumulateStores.end()) + m_LinAlgAccumulateStores.push_back( + {static_cast(Type), + static_cast( + PSVLinAlgAccumulateStoreFlag::RawBuffer), + {0, 0}}); + else + It->Flags |= + static_cast(PSVLinAlgAccumulateStoreFlag::RawBuffer); + break; + } + default: + IsSpecializedUse = false; + break; + } + + if (!IsSpecializedUse && + OP::IsDxilOpLinAlgFunc(CI->getCalledFunction())) { + CollectConstruction(CI->getType()); + for (unsigned ArgIndex = 0; ArgIndex < CI->getNumArgOperands(); + ++ArgIndex) + CollectConstruction(CI->getArgOperand(ArgIndex)->getType()); + } + } + } + } + + for (const auto &Entry : ConstructionShapes) { + const auto &Indexes = Entry.second; + m_LinAlgConstructions.push_back( + {{AddShapeIndexArray(Indexes), static_cast(Indexes.size())}, + Entry.first.first, + {0, 0, 0}}); + } + for (const auto &Entry : MultiplyShapes) { + const auto &Indexes = Entry.second; + PSVLinAlgMatrixShapeArrayReference Shapes = { + AddShapeIndexArray(Indexes), static_cast(Indexes.size())}; + uint8_t Scope = std::get<0>(Entry.first); + if (Scope == static_cast(DXIL::MatrixScope::Wave)) + m_LinAlgWaveMatrixMultiplies.push_back( + {Shapes, std::get<1>(Entry.first), std::get<2>(Entry.first), + std::get<3>(Entry.first), 0}); + else + m_LinAlgThreadGroupMatrixMultiplies.push_back( + {Shapes, std::get<1>(Entry.first), std::get<2>(Entry.first), + std::get<3>(Entry.first), 0}); + } + + m_PSVInitInfo.LinAlgMatrixOperationShapes = m_LinAlgShapes.data(); + m_PSVInitInfo.LinAlgMatrixOperationShapeCount = m_LinAlgShapes.size(); + m_PSVInitInfo.LinAlgMatrixConstructions = m_LinAlgConstructions.data(); + m_PSVInitInfo.LinAlgMatrixConstructionCount = m_LinAlgConstructions.size(); + m_PSVInitInfo.LinAlgThreadMatrixVectorMultiplies = + m_LinAlgThreadMatrixVectorMultiplies.data(); + m_PSVInitInfo.LinAlgThreadMatrixVectorMultiplyCount = + m_LinAlgThreadMatrixVectorMultiplies.size(); + m_PSVInitInfo.LinAlgWaveMatrixMultiplies = + m_LinAlgWaveMatrixMultiplies.data(); + m_PSVInitInfo.LinAlgWaveMatrixMultiplyCount = + m_LinAlgWaveMatrixMultiplies.size(); + m_PSVInitInfo.LinAlgThreadGroupMatrixMultiplies = + m_LinAlgThreadGroupMatrixMultiplies.data(); + m_PSVInitInfo.LinAlgThreadGroupMatrixMultiplyCount = + m_LinAlgThreadGroupMatrixMultiplies.size(); + m_PSVInitInfo.LinAlgOuterProducts = m_LinAlgOuterProducts.data(); + m_PSVInitInfo.LinAlgOuterProductCount = m_LinAlgOuterProducts.size(); + m_PSVInitInfo.LinAlgAccumulateStores = m_LinAlgAccumulateStores.data(); + m_PSVInitInfo.LinAlgAccumulateStoreCount = m_LinAlgAccumulateStores.size(); + } + void SetPSVSigElement(PSVSignatureElement0 &E, const DxilSignatureElement &SE) { memset(&E, 0, sizeof(PSVSignatureElement0)); @@ -771,6 +1172,11 @@ class DxilPSVWriter : public DxilPartWriter { Name.size()); } + if (m_PSVInitInfo.PSVVersion > 3) { + LoadLinAlgMatrixInfos(); + CollectLinAlgRuntimeInfo(); + } + // Set String and SemanticInput Tables m_PSVInitInfo.StringTable.Table = m_StringBuffer.data(); m_PSVInitInfo.StringTable.Size = m_StringBuffer.size(); diff --git a/lib/DxilValidation/DxilContainerValidation.cpp b/lib/DxilValidation/DxilContainerValidation.cpp index 9aa2633624..e58a3e2069 100644 --- a/lib/DxilValidation/DxilContainerValidation.cpp +++ b/lib/DxilValidation/DxilContainerValidation.cpp @@ -137,7 +137,7 @@ class SemanticIndexTableVerifier { return false; if (Offset > Table.Entries) return false; - if ((Offset + Size) > Table.Entries) + if (Size > Table.Entries - Offset) return false; for (unsigned i = Offset; i < (Offset + Size); ++i) { UseMask[i] = true; @@ -180,6 +180,7 @@ class PSVContentVerifier { PSVSignatureElement0 *, const PSVStringTable &, const PSVSemanticIndexTable &, std::string, bool); void VerifyResources(unsigned PSVVersion); + void VerifyLinAlgRuntimeInfo(unsigned PSVVersion); template void VerifyResourceTable(T &ResTab, unsigned &ResourceIndex, unsigned PSVVersion); @@ -465,6 +466,190 @@ void PSVContentVerifier::VerifyEntryProperties( } } +void PSVContentVerifier::VerifyLinAlgRuntimeInfo(unsigned PSVVersion) { + // Regenerate the expected runtime info to compare the container + // contents against + unique_ptr pWriter(NewPSVWriter(DM, PSVVersion)); + CComPtr pOutputStream; + IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pOutputStream)); + pOutputStream->Reserve(pWriter->size()); + pWriter->write(pOutputStream); + + DxilPipelineStateValidation ExpectedPSV; + if (!ExpectedPSV.InitFromPSV0(pOutputStream->GetPtr(), + pOutputStream->GetPtrSize())) { + ValCtx.EmitFormatError( + ValidationRule::ContainerPartMatches, + {"Pipeline State Validation generated from DxilModule"}); + return; + } + + bool HasLinAlgRuntimeInfo = PSV.GetPSVLinAlgRuntimeInfo0() != nullptr; + bool ExpectedHasLinAlgRuntimeInfo = + ExpectedPSV.GetPSVLinAlgRuntimeInfo0() != nullptr; + if (HasLinAlgRuntimeInfo != ExpectedHasLinAlgRuntimeInfo) { + EmitMismatchError("LinAlgRuntimeInfoPresent", + HasLinAlgRuntimeInfo ? "true" : "false", + ExpectedHasLinAlgRuntimeInfo ? "true" : "false"); + return; + } + + if (!HasLinAlgRuntimeInfo) + return; + + auto VerifyShapeReference = + [&](StringRef Name, const PSVLinAlgMatrixShapeArrayReference &ShapeRef, + const PSVLinAlgMatrixShapeArrayReference *ExpectedShapeRef) { + if (!IndexTableVerifier.MarkUse(ShapeRef.ShapesIndex, ShapeRef.Count)) { + EmitInvalidError("LinAlgOperationShapes"); + return; + } + const uint32_t *ShapeIndexes = + PSV.GetSemanticIndexTable().Get(ShapeRef.ShapesIndex); + for (uint32_t I = 0; I < ShapeRef.Count; ++I) { + if (!PSV.GetPSVLinAlgMatrixOperationShape(ShapeIndexes[I])) { + EmitInvalidError("LinAlgOperationShapeIndex"); + return; + } + } + + if (!ExpectedShapeRef) + return; + if (ShapeRef.Count != ExpectedShapeRef->Count) { + EmitMismatchError((Name + "Count").str(), + std::to_string(ShapeRef.Count), + std::to_string(ExpectedShapeRef->Count)); + return; + } + if (ShapeRef.Count == 0) + return; + + const PSVSemanticIndexTable &ExpectedIndexTable = + ExpectedPSV.GetSemanticIndexTable(); + if (ExpectedIndexTable.Table == nullptr || + ExpectedShapeRef->ShapesIndex > ExpectedIndexTable.Entries || + ExpectedShapeRef->Count > + ExpectedIndexTable.Entries - ExpectedShapeRef->ShapesIndex) { + EmitMismatchError( + Name, "valid shape index sequence", + "invalid shape index sequence generated from DxilModule"); + return; + } + + const uint32_t *ExpectedShapeIndexes = + ExpectedIndexTable.Get(ExpectedShapeRef->ShapesIndex); + if (!std::equal(ShapeIndexes, ShapeIndexes + ShapeRef.Count, + ExpectedShapeIndexes)) + EmitMismatchError(Name, "shape index sequence", + "shape index sequence generated from DxilModule"); + }; + + auto GetRecordName = [](StringRef Name, uint32_t I) { + return Name.str() + "[" + std::to_string(I) + "]"; + }; + + auto GetRecordBytes = [](const auto &Record) { + static constexpr char HexDigits[] = "0123456789abcdef"; + const uint8_t *Bytes = reinterpret_cast(&Record); + std::string Result; + Result.reserve(sizeof(Record) * 3 - 1); + for (size_t I = 0; I < sizeof(Record); ++I) { + if (I != 0) + Result.push_back(' '); + Result.push_back(HexDigits[Bytes[I] >> 4]); + Result.push_back(HexDigits[Bytes[I] & 0xf]); + } + return Result; + }; + + auto VerifyRecord = [&](StringRef Name, uint32_t I, const auto &Record, + const auto *ExpectedRecord) { + if (ExpectedRecord && memcmp(&Record, ExpectedRecord, sizeof(Record)) != 0) + EmitMismatchError(GetRecordName(Name, I), GetRecordBytes(Record), + GetRecordBytes(*ExpectedRecord)); + }; + + auto VerifyRecordWithShapes = [&](StringRef Name, uint32_t I, + const auto &Record, + const auto *ExpectedRecord) { + if (ExpectedRecord) { + auto ComparableRecord = Record; + auto ComparableExpectedRecord = *ExpectedRecord; + ComparableRecord.OperationShapes = {}; + ComparableExpectedRecord.OperationShapes = {}; + if (memcmp(&ComparableRecord, &ComparableExpectedRecord, + sizeof(ComparableRecord)) != 0) + EmitMismatchError(GetRecordName(Name, I), + GetRecordBytes(ComparableRecord), + GetRecordBytes(ComparableExpectedRecord)); + } + + std::string ShapeName = Name.str() + "OperationShapes"; + VerifyShapeReference(ShapeName, Record.OperationShapes, + ExpectedRecord ? &ExpectedRecord->OperationShapes + : nullptr); + }; + + auto VerifyLinAlgTable = [&](StringRef Name, auto CountMethod, auto GetMethod, + auto VerifyTableRecord) { + uint32_t Count = (PSV.*CountMethod)(); + uint32_t ExpectedCount = (ExpectedPSV.*CountMethod)(); + if (Count != ExpectedCount) + EmitMismatchError(Name.str() + "Count", std::to_string(Count), + std::to_string(ExpectedCount)); + + for (uint32_t I = 0; I < Count; ++I) { + const auto *Record = (PSV.*GetMethod)(I); + const auto *ExpectedRecord = + I < ExpectedCount ? (ExpectedPSV.*GetMethod)(I) : nullptr; + if (!Record) { + EmitMismatchError(GetRecordName(Name, I), "missing record", + ExpectedRecord ? GetRecordBytes(*ExpectedRecord) + : "record generated from DxilModule"); + continue; + } + if (I < ExpectedCount && !ExpectedRecord) + EmitMismatchError(GetRecordName(Name, I), GetRecordBytes(*Record), + "missing record generated from DxilModule"); + VerifyTableRecord(Name, I, *Record, ExpectedRecord); + } + }; + + VerifyLinAlgTable( + "LinAlgMatrixOperationShape", + &DxilPipelineStateValidation::GetPSVLinAlgMatrixOperationShapeCount, + &DxilPipelineStateValidation::GetPSVLinAlgMatrixOperationShape, + VerifyRecord); + VerifyLinAlgTable( + "LinAlgMatrixConstruction", + &DxilPipelineStateValidation::GetPSVLinAlgMatrixConstructionCount, + &DxilPipelineStateValidation::GetPSVLinAlgMatrixConstruction, + VerifyRecordWithShapes); + VerifyLinAlgTable( + "LinAlgThreadMatrixVectorMultiply", + &DxilPipelineStateValidation::GetPSVLinAlgThreadMatrixVectorMultiplyCount, + &DxilPipelineStateValidation::GetPSVLinAlgThreadMatrixVectorMultiply, + VerifyRecord); + VerifyLinAlgTable( + "LinAlgWaveMatrixMultiply", + &DxilPipelineStateValidation::GetPSVLinAlgWaveMatrixMultiplyCount, + &DxilPipelineStateValidation::GetPSVLinAlgWaveMatrixMultiply, + VerifyRecordWithShapes); + VerifyLinAlgTable( + "LinAlgThreadGroupMatrixMultiply", + &DxilPipelineStateValidation::GetPSVLinAlgThreadGroupMatrixMultiplyCount, + &DxilPipelineStateValidation::GetPSVLinAlgThreadGroupMatrixMultiply, + VerifyRecordWithShapes); + VerifyLinAlgTable("LinAlgOuterProduct", + &DxilPipelineStateValidation::GetPSVLinAlgOuterProductCount, + &DxilPipelineStateValidation::GetPSVLinAlgOuterProduct, + VerifyRecord); + VerifyLinAlgTable( + "LinAlgAccumulateStore", + &DxilPipelineStateValidation::GetPSVLinAlgAccumulateStoreCount, + &DxilPipelineStateValidation::GetPSVLinAlgAccumulateStore, VerifyRecord); +} + void PSVContentVerifier::Verify(unsigned ValMajor, unsigned ValMinor, unsigned PSVVersion) { PSVInitInfo PSVInfo(PSVVersion); @@ -521,6 +706,8 @@ void PSVContentVerifier::Verify(unsigned ValMajor, unsigned ValMinor, DM.GetEntryFunctionName()); } } + if (PSVVersion > 3) + VerifyLinAlgRuntimeInfo(PSVVersion); StrTableVerifier.Verify(ValCtx); IndexTableVerifier.Verify(ValCtx); @@ -607,6 +794,8 @@ bool VerifySignatureMatches(llvm::Module *pModule, DXIL::SignatureKind SigKind, } struct SimplePSV { + static bool IsDwordAligned(uint32_t Size) { return (Size & 3) == 0; } + uint32_t PSVRuntimeInfoSize = 0; uint32_t PSVNumResources = 0; uint32_t PSVResourceBindInfoSize = 0; @@ -651,7 +840,7 @@ struct SimplePSV { StringTableSize = GetUint32AtOffset(pPSVData, Offset); INCREMENT_POS(4); // Make sure StringTableSize is aligned to 4 bytes. - if ((StringTableSize & 3) != 0) { + if (!IsDwordAligned(StringTableSize)) { IsValid = false; return; } @@ -742,7 +931,8 @@ struct SimplePSV { if (!Count) return true; uint32_t RecordSize = 0; - if (!ReadUint32(RecordSize) || RecordSize < MinimumRecordSize) + if (!ReadUint32(RecordSize) || !IsDwordAligned(RecordSize) || + RecordSize < MinimumRecordSize) return false; if (Offset > PSVSize || Count > (PSVSize - Offset) / RecordSize) return false; @@ -752,6 +942,7 @@ struct SimplePSV { uint32_t LinAlgRuntimeInfoSize = 0; if (!ReadUint32(LinAlgRuntimeInfoSize) || + !IsDwordAligned(LinAlgRuntimeInfoSize) || LinAlgRuntimeInfoSize < sizeof(PSVLinAlgRuntimeInfo0) || Offset > PSVSize || LinAlgRuntimeInfoSize > PSVSize - Offset) { IsValid = false; diff --git a/tools/clang/test/DXC/dumpPSV_LinAlg.hlsl b/tools/clang/test/DXC/dumpPSV_LinAlg.hlsl new file mode 100644 index 0000000000..cc89fa5f65 --- /dev/null +++ b/tools/clang/test/DXC/dumpPSV_LinAlg.hlsl @@ -0,0 +1,58 @@ +// REQUIRES: dxil-1-10 +// RUN: %dxc -enable-16bit-types -E main -T cs_6_10 %s -Fo %t +// RUN: %dxa %t -dumppsv | FileCheck %s + +#include +using namespace dx::linalg; + +ByteAddressBuffer Input : register(t0); +RWByteAddressBuffer Output : register(u0); +RWStructuredBuffer > VectorOutput : register(u1); +groupshared uint8_t4_packed SharedOutput[64]; + +using ThreadA = + Matrix; +using WaveA = + Matrix; +using WaveB = + Matrix; +using WaveAccumulator = + Matrix; +using ThreadAccumulator = Matrix; + +[numthreads(4, 4, 1)] +void main(uint Index : SV_GroupIndex) { + ThreadA TA = + ThreadA::Load(Input, 0, 0); + VectorOutput[Index] = Multiply(TA, (vector)1.0h); + + WaveA A = WaveA::Splat(1.0h); + WaveB B = WaveB::Splat(2); + WaveAccumulator C = Multiply(A, B); + C.Store(Output, 0, 20, MatrixLayout::RowMajor); + C.InterlockedAccumulate(SharedOutput, 0, 16, MatrixLayout::RowMajor); + + ThreadAccumulator Outer = + OuterProduct((float4)1.0f, (float4)2.0f); + Outer.InterlockedAccumulate(Output, 256); + InterlockedAccumulate(Output, 512, (int4)Index); +} + +// CHECK: LinAlgRuntimeInfoPresent: true +// CHECK: PSVLinAlgRuntimeInfo: +// CHECK-NEXT: MatrixOperationShapeCount: 4 +// CHECK-NEXT: MatrixConstructionCount: 3 +// CHECK-NEXT: ThreadMatrixVectorMultiplyCount: 1 +// CHECK-NEXT: WaveMatrixMultiplyCount: 1 +// CHECK-NEXT: ThreadGroupMatrixMultiplyCount: 0 +// CHECK-NEXT: OuterProductCount: 1 +// CHECK-NEXT: AccumulateStoreCount: 2 +// CHECK-NEXT: MatrixConstruction[0]: MatrixType=4, Shapes=[(0,5,4)] +// CHECK-NEXT: MatrixConstruction[1]: MatrixType=8, Shapes=[(3,0,4)] +// CHECK-NEXT: MatrixConstruction[2]: MatrixType=9, Shapes=[(3,5,0)] +// CHECK-NEXT: ThreadMatrixVectorMultiply[0]: ResultType=8, MatrixType=8, VectorInputType=8, Flags=1 +// CHECK-NEXT: WaveMatrixMultiply[0]: AccumulatorType=9, MatrixAType=8, MatrixBType=4, Shapes=[(3,5,4)] +// CHECK-NEXT: OuterProduct[0]: ResultType=9, VectorInputType=9 +// CHECK-NEXT: AccumulateStore[0]: AccumulatorType=9, Flags=3 +// CHECK-NEXT: AccumulateStore[1]: AccumulatorType=4, Flags=1 diff --git a/tools/clang/test/DXC/dumpPSV_LinAlgAccumulate.hlsl b/tools/clang/test/DXC/dumpPSV_LinAlgAccumulate.hlsl new file mode 100644 index 0000000000..35b6980b84 --- /dev/null +++ b/tools/clang/test/DXC/dumpPSV_LinAlgAccumulate.hlsl @@ -0,0 +1,69 @@ +// REQUIRES: dxil-1-10 +// RUN: %dxc -enable-16bit-types -E main -T cs_6_10 %s -Fo %t +// RUN: %dxa %t -dumppsv | FileCheck %s + +#include +using namespace dx::linalg; + +RWByteAddressBuffer Output : register(u0); +groupshared half SharedHalf[64]; +groupshared float SharedFloat[64]; + +using ThreadHalfAccumulator = Matrix; +using ThreadFloatAccumulator = Matrix; +using ThreadIntAccumulator = Matrix; +using WaveHalfAccumulator = Matrix; +using WaveFloatAccumulator = Matrix; + +[numthreads(4, 4, 1)] +void main(uint Index : SV_GroupIndex) { + ThreadHalfAccumulator HalfOuter = + OuterProduct((vector)1.0h, + (vector)2.0h); + HalfOuter.InterlockedAccumulate(Output, 0); + + ThreadFloatAccumulator FloatOuter = + OuterProduct((vector)3.0h, + (vector)4.0h); + FloatOuter.InterlockedAccumulate(Output, 64); + + ThreadIntAccumulator IntOuter = + OuterProduct((int4)5, (int4)6); + IntOuter.InterlockedAccumulate(Output, 128); + + WaveHalfAccumulator WaveHalf = WaveHalfAccumulator::Splat(7.0h); + WaveHalf.InterlockedAccumulate(Output, 192, 4, MatrixLayout::RowMajor); + WaveHalf.InterlockedAccumulate(SharedHalf, 0, 8, MatrixLayout::RowMajor); + + WaveFloatAccumulator WaveFloat = WaveFloatAccumulator::Splat(8.0f); + WaveFloat.InterlockedAccumulate(SharedFloat, 0, 4, MatrixLayout::RowMajor); + + InterlockedAccumulate(Output, 256, (vector)Index); +} + +// CHECK: LinAlgRuntimeInfoPresent: true +// CHECK: PSVLinAlgRuntimeInfo: +// CHECK-NEXT: MatrixOperationShapeCount: 1 +// CHECK-NEXT: MatrixConstructionCount: 2 +// CHECK-NEXT: ThreadMatrixVectorMultiplyCount: 0 +// CHECK-NEXT: WaveMatrixMultiplyCount: 0 +// CHECK-NEXT: ThreadGroupMatrixMultiplyCount: 0 +// CHECK-NEXT: OuterProductCount: 3 +// CHECK-NEXT: AccumulateStoreCount: 4 +// CHECK-NEXT: MatrixConstruction[0]: MatrixType=8, Shapes=[(2,2,0)] +// CHECK-NEXT: MatrixConstruction[1]: MatrixType=9, Shapes=[(2,2,0)] +// CHECK-NEXT: OuterProduct[0]: ResultType=8, VectorInputType=8 +// CHECK-NEXT: OuterProduct[1]: ResultType=9, VectorInputType=8 +// CHECK-NEXT: OuterProduct[2]: ResultType=4, VectorInputType=4 +// CHECK-NEXT: AccumulateStore[0]: AccumulatorType=8, Flags=3 +// CHECK-NEXT: AccumulateStore[1]: AccumulatorType=9, Flags=3 +// CHECK-NEXT: AccumulateStore[2]: AccumulatorType=4, Flags=1 +// CHECK-NEXT: AccumulateStore[3]: AccumulatorType=6, Flags=1 diff --git a/tools/clang/test/DXC/dumpPSV_LinAlgConstructions.hlsl b/tools/clang/test/DXC/dumpPSV_LinAlgConstructions.hlsl new file mode 100644 index 0000000000..4b86ae86de --- /dev/null +++ b/tools/clang/test/DXC/dumpPSV_LinAlgConstructions.hlsl @@ -0,0 +1,47 @@ +// REQUIRES: dxil-1-10 +// RUN: %dxc -E main -T cs_6_10 %s -Fo %t +// RUN: %dxa %t -dumppsv | FileCheck %s + +#include +using namespace dx::linalg; + +RWByteAddressBuffer Output : register(u0); + +using WaveA0 = + Matrix; +using GroupA1 = + Matrix; +using WaveB0 = + Matrix; +using GroupB1 = + Matrix; +using WaveAccumulator0 = Matrix; +using GroupAccumulator1 = + Matrix; + +[numthreads(4, 4, 1)] +void main() { + WaveA0::Splat(1.0f).Store(Output, 0, 16, MatrixLayout::RowMajor); + GroupA1::Splat(2.0f).Store(Output, 64, 20, MatrixLayout::RowMajor); + WaveB0::Splat(3.0f).Store(Output, 128, 16, MatrixLayout::RowMajor); + GroupB1::Splat(4.0f).Store(Output, 192, 24, MatrixLayout::RowMajor); + WaveAccumulator0::Splat(5.0f).Store(Output, 256, 16, + MatrixLayout::RowMajor); + GroupAccumulator1::Splat(6.0f).Store(Output, 320, 24, + MatrixLayout::RowMajor); +} + +// CHECK: LinAlgRuntimeInfoPresent: true +// CHECK: PSVLinAlgRuntimeInfo: +// CHECK-NEXT: MatrixOperationShapeCount: 6 +// CHECK-NEXT: MatrixConstructionCount: 3 +// CHECK-NEXT: ThreadMatrixVectorMultiplyCount: 0 +// CHECK-NEXT: WaveMatrixMultiplyCount: 0 +// CHECK-NEXT: ThreadGroupMatrixMultiplyCount: 0 +// CHECK-NEXT: OuterProductCount: 0 +// CHECK-NEXT: AccumulateStoreCount: 0 +// CHECK-NEXT: MatrixConstruction[0]: MatrixType=9, Shapes=[(2,0,4), (4,0,5)] +// CHECK-NEXT: MatrixConstruction[1]: MatrixType=9, Shapes=[(0,4,4), (0,6,5)] +// CHECK-NEXT: MatrixConstruction[2]: MatrixType=9, Shapes=[(2,4,0), (4,6,0)] diff --git a/tools/clang/test/DXC/dumpPSV_LinAlgMatVec.hlsl b/tools/clang/test/DXC/dumpPSV_LinAlgMatVec.hlsl new file mode 100644 index 0000000000..2b8515bee6 --- /dev/null +++ b/tools/clang/test/DXC/dumpPSV_LinAlgMatVec.hlsl @@ -0,0 +1,60 @@ +// REQUIRES: dxil-1-10 +// RUN: %dxc -enable-16bit-types -E main -T cs_6_10 %s -Fo %t +// RUN: %dxa %t -dumppsv | FileCheck %s + +#include +using namespace dx::linalg; + +ByteAddressBuffer Input : register(t0); +RWStructuredBuffer > HalfOutput : register(u0); +RWStructuredBuffer FloatOutput : register(u1); +RWStructuredBuffer IntOutput : register(u2); +RWStructuredBuffer UintOutput : register(u3); + +using HalfA = + Matrix; +using FloatA = + Matrix; +using IntA = + Matrix; + +[numthreads(4, 4, 1)] +void main(uint Index : SV_GroupIndex) { + HalfA MulOptimal = + HalfA::Load(Input, 0, 0); + HalfOutput[Index] = Multiply(MulOptimal, (vector)1.0h); + + FloatA Transposed = + FloatA::Load(Input, 64, 0); + FloatOutput[Index] = + MultiplyAdd(Transposed, (float4)2.0f, (float4)3.0f); + + IntA RowMajor = + IntA::Load(Input, 128, 16); + IntOutput[Index] = Multiply(RowMajor, (int4)4); + + IntA MaybeTransposed = + IntA::Load(Input, 192, 0); + IntA MaybeRowMajor = + IntA::Load(Input, 256, 16); + IntA Selected = MaybeTransposed; + if (Index) + Selected = MaybeRowMajor; + InterpretedVector UnsignedInput = + MakeInterpretedVector((uint4)5); + UintOutput[Index] = Multiply(Selected, UnsignedInput); +} + +// CHECK: LinAlgRuntimeInfoPresent: true +// CHECK: PSVLinAlgRuntimeInfo: +// CHECK-NEXT: MatrixOperationShapeCount: 0 +// CHECK-NEXT: MatrixConstructionCount: 0 +// CHECK-NEXT: ThreadMatrixVectorMultiplyCount: 4 +// CHECK-NEXT: WaveMatrixMultiplyCount: 0 +// CHECK-NEXT: ThreadGroupMatrixMultiplyCount: 0 +// CHECK-NEXT: OuterProductCount: 0 +// CHECK-NEXT: AccumulateStoreCount: 0 +// CHECK-NEXT: ThreadMatrixVectorMultiply[0]: ResultType=8, MatrixType=8, VectorInputType=8, Flags=0 +// CHECK-NEXT: ThreadMatrixVectorMultiply[1]: ResultType=9, MatrixType=9, VectorInputType=9, Flags=1 +// CHECK-NEXT: ThreadMatrixVectorMultiply[2]: ResultType=4, MatrixType=4, VectorInputType=4, Flags=2 +// CHECK-NEXT: ThreadMatrixVectorMultiply[3]: ResultType=5, MatrixType=4, VectorInputType=5, Flags=3 diff --git a/tools/clang/test/DXC/dumpPSV_LinAlgMatrixMultiply.hlsl b/tools/clang/test/DXC/dumpPSV_LinAlgMatrixMultiply.hlsl new file mode 100644 index 0000000000..39d622c36d --- /dev/null +++ b/tools/clang/test/DXC/dumpPSV_LinAlgMatrixMultiply.hlsl @@ -0,0 +1,66 @@ +// REQUIRES: dxil-1-10 +// RUN: %dxc -enable-16bit-types -E main -T cs_6_10 %s -Fo %t +// RUN: %dxa %t -dumppsv | FileCheck %s + +#include +using namespace dx::linalg; + +RWByteAddressBuffer Output : register(u0); + +using WaveA0 = + Matrix; +using WaveB0 = + Matrix; +using WaveAccumulator0 = Matrix; +using WaveA1 = + Matrix; +using WaveB1 = + Matrix; +using WaveAccumulator1 = Matrix; + +using GroupA = + Matrix; +using GroupB = + Matrix; +using GroupAccumulator = + Matrix; + +[numthreads(4, 4, 1)] +void main() { + WaveA0 A0 = WaveA0::Splat(1.0h); + WaveB0 B0 = WaveB0::Splat(2); + WaveAccumulator0 C0 = Multiply(A0, B0); + C0.Store(Output, 0, 16, MatrixLayout::RowMajor); + + WaveA1 A1 = WaveA1::Splat(3.0h); + WaveB1 B1 = WaveB1::Splat(4); + WaveAccumulator1 C1 = WaveAccumulator1::Splat(5.0f); + C1.MultiplyAccumulate(A1, B1); + C1.Store(Output, 128, 24, MatrixLayout::RowMajor); + + GroupA GA = GroupA::Splat(6); + GroupB GB = GroupB::Splat(7u); + GroupAccumulator GC = Multiply(GA, GB); + GC.Store(Output, 256, 20, MatrixLayout::RowMajor); +} + +// CHECK: LinAlgRuntimeInfoPresent: true +// CHECK: PSVLinAlgRuntimeInfo: +// CHECK-NEXT: MatrixOperationShapeCount: 12 +// CHECK-NEXT: MatrixConstructionCount: 6 +// CHECK-NEXT: ThreadMatrixVectorMultiplyCount: 0 +// CHECK-NEXT: WaveMatrixMultiplyCount: 1 +// CHECK-NEXT: ThreadGroupMatrixMultiplyCount: 1 +// CHECK-NEXT: OuterProductCount: 0 +// CHECK-NEXT: AccumulateStoreCount: 0 +// CHECK-NEXT: MatrixConstruction[0]: MatrixType=4, Shapes=[(3,0,4)] +// CHECK-NEXT: MatrixConstruction[1]: MatrixType=4, Shapes=[(0,4,4), (0,6,7)] +// CHECK-NEXT: MatrixConstruction[2]: MatrixType=4, Shapes=[(3,5,0)] +// CHECK-NEXT: MatrixConstruction[3]: MatrixType=5, Shapes=[(0,5,4)] +// CHECK-NEXT: MatrixConstruction[4]: MatrixType=8, Shapes=[(2,0,4), (5,0,7)] +// CHECK-NEXT: MatrixConstruction[5]: MatrixType=9, Shapes=[(2,4,0), (5,6,0)] +// CHECK-NEXT: WaveMatrixMultiply[0]: AccumulatorType=9, MatrixAType=8, MatrixBType=4, Shapes=[(2,4,4), (5,6,7)] +// CHECK-NEXT: ThreadGroupMatrixMultiply[0]: AccumulatorType=4, MatrixAType=4, MatrixBType=5, Shapes=[(3,5,4)] diff --git a/tools/clang/unittests/HLSL/ValidationTest.cpp b/tools/clang/unittests/HLSL/ValidationTest.cpp index 96b07318bf..8df9dcfb73 100644 --- a/tools/clang/unittests/HLSL/ValidationTest.cpp +++ b/tools/clang/unittests/HLSL/ValidationTest.cpp @@ -326,6 +326,7 @@ class ValidationTest : public ::testing::Test { TEST_METHOD(PSVContentValidationCS) TEST_METHOD(PSVContentValidationMS) TEST_METHOD(PSVContentValidationAS) + TEST_METHOD(PSVContentValidationLinAlg) TEST_METHOD(UnitTestExtValidationSupport) TEST_METHOD(WrongPSVSize) TEST_METHOD(WrongPSVSizeOnZeros) @@ -6429,6 +6430,104 @@ TEST_F(ValidationTest, PSVContentValidationAS) { /*maySucceedAnyway*/ false, /*bRegex*/ false); } +TEST_F(ValidationTest, PSVContentValidationLinAlg) { + if (m_ver.SkipDxilVersion(1, 10)) + return; + + CComPtr pProgram; + CompileFile(L"..\\DXC\\dumpPSV_LinAlgConstructions.hlsl", "cs_6_10", + &pProgram); + + CComPtr pValidator; + VERIFY_SUCCEEDED( + m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator)); + + auto ValidateFailure = [&](LPCSTR ExpectedError) { + CComPtr pResult; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, 0, &pResult)); + VERIFY_IS_NOT_NULL(pResult); + HRESULT Status; + VERIFY_SUCCEEDED(pResult->GetStatus(&Status)); + VERIFY_FAILED(Status); + CheckOperationResultMsgs(pResult, {ExpectedError}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); + }; + + hlsl::DxilContainerHeader *pHeader = + static_cast(pProgram->GetBufferPointer()); + DxilPartHeader *pPSVPart = + GetDxilPartByType(pHeader, hlsl::DFCC_PipelineStateValidation); + VERIFY_IS_NOT_NULL(pPSVPart); + + DxilPipelineStateValidation PSV; + VERIFY_IS_TRUE( + PSV.InitFromPSV0(GetDxilPartData(pPSVPart), pPSVPart->PartSize)); + + PSVLinAlgRuntimeInfo0 *LinAlgRuntimeInfo = PSV.GetPSVLinAlgRuntimeInfo0(); + PSVLinAlgMatrixOperationShape0 *Shape = + PSV.GetPSVLinAlgMatrixOperationShape(0); + PSVLinAlgMatrixConstruction0 *Construction = + PSV.GetPSVLinAlgMatrixConstruction(0); + VERIFY_IS_NOT_NULL(LinAlgRuntimeInfo); + VERIFY_IS_NOT_NULL(Shape); + VERIFY_IS_NOT_NULL(Construction); + VERIFY_ARE_EQUAL(6u, PSV.GetPSVLinAlgMatrixOperationShapeCount()); + VERIFY_ARE_EQUAL(3u, PSV.GetPSVLinAlgMatrixConstructionCount()); + VERIFY_ARE_EQUAL(2u, Construction->OperationShapes.Count); + + uint32_t OriginalConstructionCount = + LinAlgRuntimeInfo->MatrixConstructionCount; + uint32_t *ConstructionRecordSize = + reinterpret_cast(Construction) - 1; + uint32_t OriginalConstructionRecordSize = *ConstructionRecordSize; + LinAlgRuntimeInfo->MatrixConstructionCount = 1; + *ConstructionRecordSize = + OriginalConstructionRecordSize * OriginalConstructionCount; + ValidateFailure( + "DXIL container mismatch for 'LinAlgMatrixConstructionCount'"); + LinAlgRuntimeInfo->MatrixConstructionCount = OriginalConstructionCount; + *ConstructionRecordSize = OriginalConstructionRecordSize; + + ++Shape->M; + ValidateFailure( + "DXIL container mismatch for 'LinAlgMatrixOperationShape[0]'"); + --Shape->M; + + ++Construction->MatrixType; + ValidateFailure("DXIL container mismatch for 'LinAlgMatrixConstruction[0]'"); + --Construction->MatrixType; + + uint32_t OriginalShapeCount = Construction->OperationShapes.Count; + --Construction->OperationShapes.Count; + ValidateFailure("DXIL container mismatch for " + "'LinAlgMatrixConstructionOperationShapesCount'"); + Construction->OperationShapes.Count = OriginalShapeCount; + + uint32_t OriginalShapesIndex = Construction->OperationShapes.ShapesIndex; + Construction->OperationShapes.ShapesIndex = + PSV.GetSemanticIndexTable().Entries + 1; + ValidateFailure("In 'PSV0 part', 'LinAlgOperationShapes' is not well-formed"); + Construction->OperationShapes.ShapesIndex = OriginalShapesIndex; + + uint32_t *ShapeIndexes = const_cast( + PSV.GetSemanticIndexTable().Get(OriginalShapesIndex)); + VERIFY_IS_NOT_NULL(ShapeIndexes); + uint32_t OriginalShapeIndex = ShapeIndexes[0]; + ShapeIndexes[0] = PSV.GetPSVLinAlgMatrixOperationShapeCount(); + ValidateFailure( + "In 'PSV0 part', 'LinAlgOperationShapeIndex' is not well-formed"); + ShapeIndexes[0] = OriginalShapeIndex; + + std::swap(ShapeIndexes[0], ShapeIndexes[1]); + ValidateFailure("DXIL container mismatch for " + "'LinAlgMatrixConstructionOperationShapes'"); + std::swap(ShapeIndexes[0], ShapeIndexes[1]); + + CComPtr pResult; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, 0, &pResult)); + CheckOperationResultMsgs(pResult, {}, false, false); +} + struct SimpleContainer { hlsl::DxilContainerHeader *Header; std::vector PartOffsets;