From 0e7591a6ee94c8c8eb0d536ce7815fd56a776451 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Tue, 13 Aug 2024 17:33:09 -0700 Subject: [PATCH] [NFC] Move DxilValidation to a dedicated folder. (#6849) Move DxilValidation out of HLSL. Also move code to validate dxil container into DxilContainerValidation.cpp from DxilValidation.cpp. This is a preparatory step for #6817. --------- Co-authored-by: github-actions[bot] --- include/dxc/CMakeLists.txt | 1 - .../{HLSL => DxilValidation}/DxilValidation.h | 23 +- include/dxc/HLSL/CMakeLists.txt | 1 - lib/CMakeLists.txt | 1 + lib/DxilValidation/CMakeLists.txt | 15 + .../DxilContainerValidation.cpp | 705 ++++++++++ .../DxilValidation.cpp | 1218 +---------------- lib/DxilValidation/DxilValidationUtils.cpp | 526 +++++++ lib/DxilValidation/DxilValidationUtils.h | 149 ++ lib/HLSL/CMakeLists.txt | 2 - lib/HLSL/WaveSensitivityAnalysis.cpp | 20 - .../dxilconv/tools/dxilconv/CMakeLists.txt | 4 +- tools/clang/tools/dxcompiler/dxclinker.cpp | 2 +- tools/clang/tools/dxcvalidator/CMakeLists.txt | 2 +- .../clang/tools/dxcvalidator/dxcvalidator.cpp | 2 +- .../dxcdxrfallbackcompiler.cpp | 2 +- utils/hct/hctdb_instrhelp.py | 1 - 17 files changed, 1408 insertions(+), 1266 deletions(-) rename include/dxc/{HLSL => DxilValidation}/DxilValidation.h (79%) delete mode 100644 include/dxc/HLSL/CMakeLists.txt create mode 100644 lib/DxilValidation/CMakeLists.txt create mode 100644 lib/DxilValidation/DxilContainerValidation.cpp rename lib/{HLSL => DxilValidation}/DxilValidation.cpp (82%) create mode 100644 lib/DxilValidation/DxilValidationUtils.cpp create mode 100644 lib/DxilValidation/DxilValidationUtils.h diff --git a/include/dxc/CMakeLists.txt b/include/dxc/CMakeLists.txt index 0f4f857b5b..b2bf966297 100644 --- a/include/dxc/CMakeLists.txt +++ b/include/dxc/CMakeLists.txt @@ -17,7 +17,6 @@ configure_file( add_subdirectory(DXIL) add_subdirectory(DxilContainer) -add_subdirectory(HLSL) add_subdirectory(Support) add_subdirectory(Tracing) diff --git a/include/dxc/HLSL/DxilValidation.h b/include/dxc/DxilValidation/DxilValidation.h similarity index 79% rename from include/dxc/HLSL/DxilValidation.h rename to include/dxc/DxilValidation/DxilValidation.h index 673d1ce3f7..b4f49a3515 100644 --- a/include/dxc/HLSL/DxilValidation.h +++ b/include/dxc/DxilValidation/DxilValidation.h @@ -26,29 +26,7 @@ class DiagnosticInfo; namespace hlsl { -#include "dxc/HLSL/DxilValidation.inc" - -const char *GetValidationRuleText(ValidationRule value); void GetValidationVersion(unsigned *pMajor, unsigned *pMinor); -HRESULT ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule); - -// DXIL Container Verification Functions (return false on failure) - -bool VerifySignatureMatches(llvm::Module *pModule, - hlsl::DXIL::SignatureKind SigKind, - const void *pSigData, uint32_t SigSize); - -// PSV = data for Pipeline State Validation -bool VerifyPSVMatches(llvm::Module *pModule, const void *pPSVData, - uint32_t PSVSize); - -// PSV = data for Pipeline State Validation -bool VerifyRDATMatches(llvm::Module *pModule, const void *pRDATData, - uint32_t RDATSize); - -bool VerifyFeatureInfoMatches(llvm::Module *pModule, - const void *pFeatureInfoData, - uint32_t FeatureInfoSize); // Validate the container parts, assuming supplied module is valid, loaded from // the container provided @@ -106,4 +84,5 @@ class PrintDiagnosticContext { static void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context); }; + } // namespace hlsl diff --git a/include/dxc/HLSL/CMakeLists.txt b/include/dxc/HLSL/CMakeLists.txt deleted file mode 100644 index bd595998ec..0000000000 --- a/include/dxc/HLSL/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_hlsl_hctgen(DxilValidationInc OUTPUT DxilValidation.inc BUILD_DIR) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5c77ac2460..39d5b6a8f9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(DxilPdbInfo) # HLSL Change add_subdirectory(DxilPIXPasses) # HLSL Change add_subdirectory(DxilDia) # HLSL Change add_subdirectory(DxilRootSignature) # HLSL Change +add_subdirectory(DxilValidation) # HLSL Change add_subdirectory(DxcBindingTable) # HLSL Change add_subdirectory(DxrFallback) # HLSL Change add_subdirectory(DxilCompression) # HLSL Change diff --git a/lib/DxilValidation/CMakeLists.txt b/lib/DxilValidation/CMakeLists.txt new file mode 100644 index 0000000000..f454c9bbaf --- /dev/null +++ b/lib/DxilValidation/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) Microsoft Corporation. All rights reserved. +# This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. +add_hlsl_hctgen(DxilValidationInc OUTPUT DxilValidation.inc BUILD_DIR) +add_hlsl_hctgen(DxilValidation OUTPUT DxilValidationImpl.inc BUILD_DIR) + +add_llvm_library(LLVMDxilValidation + DxilContainerValidation.cpp + DxilValidation.cpp + DxilValidationUtils.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/IR +) + +add_dependencies(LLVMDxilValidation intrinsics_gen) diff --git a/lib/DxilValidation/DxilContainerValidation.cpp b/lib/DxilValidation/DxilContainerValidation.cpp new file mode 100644 index 0000000000..48fbfce8a8 --- /dev/null +++ b/lib/DxilValidation/DxilContainerValidation.cpp @@ -0,0 +1,705 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// DxilContainerValidation.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. // +// // +// This file provides support for validating DXIL container. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "dxc/Support/FileIOHelper.h" +#include "dxc/Support/Global.h" +#include "dxc/Support/WinIncludes.h" + +#include "dxc/DxilContainer/DxilContainer.h" +#include "dxc/DxilContainer/DxilContainerAssembler.h" +#include "dxc/DxilContainer/DxilPipelineStateValidation.h" +#include "dxc/DxilContainer/DxilRuntimeReflection.h" +#include "dxc/DxilRootSignature/DxilRootSignature.h" +#include "dxc/DxilValidation/DxilValidation.h" + +#include "dxc/DXIL/DxilModule.h" +#include "dxc/DXIL/DxilUtil.h" + +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +#include "DxilValidationUtils.h" + +#include + +using std::unique_ptr; +using std::unordered_set; +using std::vector; + +namespace { + +// Utility class for setting and restoring the diagnostic context so we may +// capture errors/warnings +struct DiagRestore { + LLVMContext *Ctx = nullptr; + void *OrigDiagContext; + LLVMContext::DiagnosticHandlerTy OrigHandler; + + DiagRestore(llvm::LLVMContext &InputCtx, void *DiagContext) : Ctx(&InputCtx) { + init(DiagContext); + } + DiagRestore(Module *M, void *DiagContext) { + if (!M) + return; + Ctx = &M->getContext(); + init(DiagContext); + } + ~DiagRestore() { + if (!Ctx) + return; + Ctx->setDiagnosticHandler(OrigHandler, OrigDiagContext); + } + +private: + void init(void *DiagContext) { + OrigHandler = Ctx->getDiagnosticHandler(); + OrigDiagContext = Ctx->getDiagnosticContext(); + Ctx->setDiagnosticHandler( + hlsl::PrintDiagnosticContext::PrintDiagnosticHandler, DiagContext); + } +}; + +static void emitDxilDiag(LLVMContext &Ctx, const char *str) { + hlsl::dxilutil::EmitErrorOnContext(Ctx, str); +} +} // namespace + +namespace hlsl { + +// DXIL Container Verification Functions + +static void VerifyBlobPartMatches(ValidationContext &ValCtx, LPCSTR pName, + DxilPartWriter *pWriter, const void *pData, + uint32_t Size) { + if (!pData && pWriter->size()) { + // No blob part, but writer says non-zero size is expected. + ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, {pName}); + return; + } + + // Compare sizes + if (pWriter->size() != Size) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {pName}); + return; + } + + if (Size == 0) { + return; + } + + CComPtr pOutputStream; + IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pOutputStream)); + pOutputStream->Reserve(Size); + + pWriter->write(pOutputStream); + DXASSERT(pOutputStream->GetPtrSize() == Size, + "otherwise, DxilPartWriter misreported size"); + + if (memcmp(pData, pOutputStream->GetPtr(), Size)) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {pName}); + return; + } + + return; +} + +static void VerifySignatureMatches(ValidationContext &ValCtx, + DXIL::SignatureKind SigKind, + const void *pSigData, uint32_t SigSize) { + // Generate corresponding signature from module and memcmp + + const char *pName = nullptr; + switch (SigKind) { + case hlsl::DXIL::SignatureKind::Input: + pName = "Program Input Signature"; + break; + case hlsl::DXIL::SignatureKind::Output: + pName = "Program Output Signature"; + break; + case hlsl::DXIL::SignatureKind::PatchConstOrPrim: + if (ValCtx.DxilMod.GetShaderModel()->GetKind() == DXIL::ShaderKind::Mesh) + pName = "Program Primitive Signature"; + else + pName = "Program Patch Constant Signature"; + break; + default: + break; + } + + unique_ptr pWriter( + NewProgramSignatureWriter(ValCtx.DxilMod, SigKind)); + VerifyBlobPartMatches(ValCtx, pName, pWriter.get(), pSigData, SigSize); +} + +bool VerifySignatureMatches(llvm::Module *pModule, DXIL::SignatureKind SigKind, + const void *pSigData, uint32_t SigSize) { + ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); + VerifySignatureMatches(ValCtx, SigKind, pSigData, SigSize); + return !ValCtx.Failed; +} + +static void VerifyPSVMatches(ValidationContext &ValCtx, const void *pPSVData, + uint32_t PSVSize) { + uint32_t PSVVersion = + MAX_PSV_VERSION; // This should be set to the newest version + unique_ptr pWriter(NewPSVWriter(ValCtx.DxilMod, PSVVersion)); + // Try each version in case an earlier version matches module + while (PSVVersion && pWriter->size() != PSVSize) { + PSVVersion--; + pWriter.reset(NewPSVWriter(ValCtx.DxilMod, PSVVersion)); + } + // generate PSV data from module and memcmp + VerifyBlobPartMatches(ValCtx, "Pipeline State Validation", pWriter.get(), + pPSVData, PSVSize); +} + +static void VerifyFeatureInfoMatches(ValidationContext &ValCtx, + const void *pFeatureInfoData, + uint32_t FeatureInfoSize) { + // generate Feature Info data from module and memcmp + unique_ptr pWriter(NewFeatureInfoWriter(ValCtx.DxilMod)); + VerifyBlobPartMatches(ValCtx, "Feature Info", pWriter.get(), pFeatureInfoData, + FeatureInfoSize); +} + +// return true if the pBlob is a valid, well-formed CompilerVersion part, false +// otherwise +bool ValidateCompilerVersionPart(const void *pBlobPtr, UINT blobSize) { + // The hlsl::DxilCompilerVersion struct is always 16 bytes. (2 2-byte + // uint16's, 3 4-byte uint32's) The blob size should absolutely never be less + // than 16 bytes. + if (blobSize < sizeof(hlsl::DxilCompilerVersion)) { + return false; + } + + const hlsl::DxilCompilerVersion *pDCV = + (const hlsl::DxilCompilerVersion *)pBlobPtr; + if (pDCV->VersionStringListSizeInBytes == 0) { + // No version strings, just make sure there is no extra space. + return blobSize == sizeof(hlsl::DxilCompilerVersion); + } + + // after this point, we know VersionStringListSizeInBytes >= 1, because it is + // a UINT + + UINT EndOfVersionStringIndex = + sizeof(hlsl::DxilCompilerVersion) + pDCV->VersionStringListSizeInBytes; + // Make sure that the buffer size is large enough to contain both the DCV + // struct and the version string but not any larger than necessary + if (PSVALIGN4(EndOfVersionStringIndex) != blobSize) { + return false; + } + + const char *VersionStringsListData = + (const char *)pBlobPtr + sizeof(hlsl::DxilCompilerVersion); + UINT VersionStringListSizeInBytes = pDCV->VersionStringListSizeInBytes; + + // now make sure that any pad bytes that were added are null-terminators. + for (UINT i = VersionStringListSizeInBytes; + i < blobSize - sizeof(hlsl::DxilCompilerVersion); i++) { + if (VersionStringsListData[i] != '\0') { + return false; + } + } + + // Now, version string validation + // first, the final byte of the string should always be null-terminator so + // that the string ends + if (VersionStringsListData[VersionStringListSizeInBytes - 1] != '\0') { + return false; + } + + // construct the first string + // data format for VersionString can be see in the definition for the + // DxilCompilerVersion struct. summary: 2 strings that each end with the null + // terminator, and [0-3] null terminators after the final null terminator + StringRef firstStr(VersionStringsListData); + + // if the second string exists, attempt to construct it. + if (VersionStringListSizeInBytes > (firstStr.size() + 1)) { + StringRef secondStr(VersionStringsListData + firstStr.size() + 1); + + // the VersionStringListSizeInBytes member should be exactly equal to the + // two string lengths, plus the 2 null terminator bytes. + if (VersionStringListSizeInBytes != + firstStr.size() + secondStr.size() + 2) { + return false; + } + } else { + // the VersionStringListSizeInBytes member should be exactly equal to the + // first string length, plus the 1 null terminator byte. + if (VersionStringListSizeInBytes != firstStr.size() + 1) { + return false; + } + } + + return true; +} + +static void VerifyRDATMatches(ValidationContext &ValCtx, const void *pRDATData, + uint32_t RDATSize) { + const char *PartName = "Runtime Data (RDAT)"; + RDAT::DxilRuntimeData rdat(pRDATData, RDATSize); + if (!rdat.Validate()) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {PartName}); + return; + } + + // If DxilModule subobjects already loaded, validate these against the RDAT + // blob, otherwise, load subobject into DxilModule to generate reference RDAT. + if (!ValCtx.DxilMod.GetSubobjects()) { + auto table = rdat.GetSubobjectTable(); + if (table && table.Count() > 0) { + ValCtx.DxilMod.ResetSubobjects(new DxilSubobjects()); + if (!LoadSubobjectsFromRDAT(*ValCtx.DxilMod.GetSubobjects(), rdat)) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, + {PartName}); + return; + } + } + } + + unique_ptr pWriter(NewRDATWriter(ValCtx.DxilMod)); + VerifyBlobPartMatches(ValCtx, PartName, pWriter.get(), pRDATData, RDATSize); +} + +bool VerifyRDATMatches(llvm::Module *pModule, const void *pRDATData, + uint32_t RDATSize) { + ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); + VerifyRDATMatches(ValCtx, pRDATData, RDATSize); + return !ValCtx.Failed; +} + +bool VerifyFeatureInfoMatches(llvm::Module *pModule, + const void *pFeatureInfoData, + uint32_t FeatureInfoSize) { + ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); + VerifyFeatureInfoMatches(ValCtx, pFeatureInfoData, FeatureInfoSize); + return !ValCtx.Failed; +} + +HRESULT ValidateDxilContainerParts(llvm::Module *pModule, + llvm::Module *pDebugModule, + const DxilContainerHeader *pContainer, + uint32_t ContainerSize) { + + DXASSERT_NOMSG(pModule); + if (!pContainer || !IsValidDxilContainer(pContainer, ContainerSize)) { + return DXC_E_CONTAINER_INVALID; + } + + DxilModule *pDxilModule = DxilModule::TryGetDxilModule(pModule); + if (!pDxilModule) { + return DXC_E_IR_VERIFICATION_FAILED; + } + + ValidationContext ValCtx(*pModule, pDebugModule, *pDxilModule); + + DXIL::ShaderKind ShaderKind = pDxilModule->GetShaderModel()->GetKind(); + bool bTessOrMesh = ShaderKind == DXIL::ShaderKind::Hull || + ShaderKind == DXIL::ShaderKind::Domain || + ShaderKind == DXIL::ShaderKind::Mesh; + + std::unordered_set FourCCFound; + const DxilPartHeader *pRootSignaturePart = nullptr; + const DxilPartHeader *pPSVPart = nullptr; + + for (auto it = begin(pContainer), itEnd = end(pContainer); it != itEnd; + ++it) { + const DxilPartHeader *pPart = *it; + + char szFourCC[5]; + PartKindToCharArray(pPart->PartFourCC, szFourCC); + if (FourCCFound.find(pPart->PartFourCC) != FourCCFound.end()) { + // Two parts with same FourCC found + ValCtx.EmitFormatError(ValidationRule::ContainerPartRepeated, {szFourCC}); + continue; + } + FourCCFound.insert(pPart->PartFourCC); + + switch (pPart->PartFourCC) { + case DFCC_InputSignature: + if (ValCtx.isLibProfile) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } else { + VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Input, + GetDxilPartData(pPart), pPart->PartSize); + } + break; + case DFCC_OutputSignature: + if (ValCtx.isLibProfile) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } else { + VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Output, + GetDxilPartData(pPart), pPart->PartSize); + } + break; + case DFCC_PatchConstantSignature: + if (ValCtx.isLibProfile) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } else { + if (bTessOrMesh) { + VerifySignatureMatches(ValCtx, DXIL::SignatureKind::PatchConstOrPrim, + GetDxilPartData(pPart), pPart->PartSize); + } else { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, + {"Program Patch Constant Signature"}); + } + } + break; + case DFCC_FeatureInfo: + VerifyFeatureInfoMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); + break; + case DFCC_CompilerVersion: + // This blob is either a PDB, or a library profile + if (ValCtx.isLibProfile) { + if (!ValidateCompilerVersionPart((void *)GetDxilPartData(pPart), + pPart->PartSize)) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } + } else { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } + break; + + case DFCC_RootSignature: + pRootSignaturePart = pPart; + if (ValCtx.isLibProfile) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } + break; + case DFCC_PipelineStateValidation: + pPSVPart = pPart; + if (ValCtx.isLibProfile) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } else { + VerifyPSVMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); + } + break; + + // Skip these + case DFCC_ResourceDef: + case DFCC_ShaderStatistics: + case DFCC_PrivateData: + case DFCC_DXIL: + case DFCC_ShaderDebugInfoDXIL: + case DFCC_ShaderDebugName: + continue; + + case DFCC_ShaderHash: + if (pPart->PartSize != sizeof(DxilShaderHash)) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } + break; + + // Runtime Data (RDAT) for libraries + case DFCC_RuntimeData: + if (ValCtx.isLibProfile) { + // TODO: validate without exact binary comparison of serialized data + // - support earlier versions + // - verify no newer record versions than known here (size no larger + // than newest version) + // - verify all data makes sense and matches expectations based on + // module + VerifyRDATMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); + } else { + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, + {szFourCC}); + } + break; + + case DFCC_Container: + default: + ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, {szFourCC}); + break; + } + } + + // Verify required parts found + if (ValCtx.isLibProfile) { + if (FourCCFound.find(DFCC_RuntimeData) == FourCCFound.end()) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, + {"Runtime Data (RDAT)"}); + } + } else { + if (FourCCFound.find(DFCC_InputSignature) == FourCCFound.end()) { + VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Input, nullptr, 0); + } + if (FourCCFound.find(DFCC_OutputSignature) == FourCCFound.end()) { + VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Output, nullptr, 0); + } + if (bTessOrMesh && + FourCCFound.find(DFCC_PatchConstantSignature) == FourCCFound.end() && + pDxilModule->GetPatchConstOrPrimSignature().GetElements().size()) { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, + {"Program Patch Constant Signature"}); + } + if (FourCCFound.find(DFCC_FeatureInfo) == FourCCFound.end()) { + // Could be optional, but RS1 runtime doesn't handle this case properly. + ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, + {"Feature Info"}); + } + + // Validate Root Signature + if (pPSVPart) { + if (pRootSignaturePart) { + std::string diagStr; + raw_string_ostream DiagStream(diagStr); + try { + RootSignatureHandle RS; + RS.LoadSerialized( + (const uint8_t *)GetDxilPartData(pRootSignaturePart), + pRootSignaturePart->PartSize); + RS.Deserialize(); + IFTBOOL(VerifyRootSignatureWithShaderPSV( + RS.GetDesc(), pDxilModule->GetShaderModel()->GetKind(), + GetDxilPartData(pPSVPart), pPSVPart->PartSize, + DiagStream), + DXC_E_INCORRECT_ROOT_SIGNATURE); + } catch (...) { + ValCtx.EmitError(ValidationRule::ContainerRootSignatureIncompatible); + emitDxilDiag(pModule->getContext(), DiagStream.str().c_str()); + } + } + } else { + ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, + {"Pipeline State Validation"}); + } + } + + if (ValCtx.Failed) { + return DXC_E_MALFORMED_CONTAINER; + } + return S_OK; +} + +static HRESULT FindDxilPart(const void *pContainerBytes, uint32_t ContainerSize, + DxilFourCC FourCC, const DxilPartHeader **ppPart) { + + const DxilContainerHeader *pContainer = + IsDxilContainerLike(pContainerBytes, ContainerSize); + + if (!pContainer) { + IFR(DXC_E_CONTAINER_INVALID); + } + if (!IsValidDxilContainer(pContainer, ContainerSize)) { + IFR(DXC_E_CONTAINER_INVALID); + } + + DxilPartIterator it = + std::find_if(begin(pContainer), end(pContainer), DxilPartIsType(FourCC)); + if (it == end(pContainer)) { + IFR(DXC_E_CONTAINER_MISSING_DXIL); + } + + const DxilProgramHeader *pProgramHeader = + reinterpret_cast(GetDxilPartData(*it)); + if (!IsValidDxilProgramHeader(pProgramHeader, (*it)->PartSize)) { + IFR(DXC_E_CONTAINER_INVALID); + } + + *ppPart = *it; + return S_OK; +} + +HRESULT ValidateLoadModule(const char *pIL, uint32_t ILLength, + unique_ptr &pModule, LLVMContext &Ctx, + llvm::raw_ostream &DiagStream, unsigned bLazyLoad) { + + llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); + PrintDiagnosticContext DiagContext(DiagPrinter); + DiagRestore DR(Ctx, &DiagContext); + + std::unique_ptr pBitcodeBuf; + pBitcodeBuf.reset(llvm::MemoryBuffer::getMemBuffer( + llvm::StringRef(pIL, ILLength), "", false) + .release()); + + ErrorOr> loadedModuleResult = + bLazyLoad == 0 + ? llvm::parseBitcodeFile(pBitcodeBuf->getMemBufferRef(), Ctx, nullptr, + true /*Track Bitstream*/) + : llvm::getLazyBitcodeModule(std::move(pBitcodeBuf), Ctx, nullptr, + false, true /*Track Bitstream*/); + + // DXIL disallows some LLVM bitcode constructs, like unaccounted-for + // sub-blocks. These appear as warnings, which the validator should reject. + if (DiagContext.HasErrors() || DiagContext.HasWarnings() || + loadedModuleResult.getError()) + return DXC_E_IR_VERIFICATION_FAILED; + + pModule = std::move(loadedModuleResult.get()); + return S_OK; +} + +HRESULT ValidateDxilBitcode(const char *pIL, uint32_t ILLength, + llvm::raw_ostream &DiagStream) { + + LLVMContext Ctx; + std::unique_ptr pModule; + + llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); + PrintDiagnosticContext DiagContext(DiagPrinter); + Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, + &DiagContext, true); + + HRESULT hr; + if (FAILED(hr = ValidateLoadModule(pIL, ILLength, pModule, Ctx, DiagStream, + /*bLazyLoad*/ false))) + return hr; + + if (FAILED(hr = ValidateDxilModule(pModule.get(), nullptr))) + return hr; + + DxilModule &dxilModule = pModule->GetDxilModule(); + auto &SerializedRootSig = dxilModule.GetSerializedRootSignature(); + if (!SerializedRootSig.empty()) { + unique_ptr pWriter(NewPSVWriter(dxilModule)); + DXASSERT_NOMSG(pWriter->size()); + CComPtr pOutputStream; + IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pOutputStream)); + pOutputStream->Reserve(pWriter->size()); + pWriter->write(pOutputStream); + DxilVersionedRootSignature desc; + try { + DeserializeRootSignature(SerializedRootSig.data(), + SerializedRootSig.size(), desc.get_address_of()); + if (!desc.get()) { + return DXC_E_INCORRECT_ROOT_SIGNATURE; + } + IFTBOOL(VerifyRootSignatureWithShaderPSV( + desc.get(), dxilModule.GetShaderModel()->GetKind(), + pOutputStream->GetPtr(), pWriter->size(), DiagStream), + DXC_E_INCORRECT_ROOT_SIGNATURE); + } catch (...) { + return DXC_E_INCORRECT_ROOT_SIGNATURE; + } + } + + if (DiagContext.HasErrors() || DiagContext.HasWarnings()) { + return DXC_E_IR_VERIFICATION_FAILED; + } + + return S_OK; +} + +static HRESULT ValidateLoadModuleFromContainer( + const void *pContainer, uint32_t ContainerSize, + std::unique_ptr &pModule, + std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, + LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream, unsigned bLazyLoad) { + llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); + PrintDiagnosticContext DiagContext(DiagPrinter); + DiagRestore DR(Ctx, &DiagContext); + DiagRestore DR2(DbgCtx, &DiagContext); + + const DxilPartHeader *pPart = nullptr; + IFR(FindDxilPart(pContainer, ContainerSize, DFCC_DXIL, &pPart)); + + const char *pIL = nullptr; + uint32_t ILLength = 0; + GetDxilProgramBitcode( + reinterpret_cast(GetDxilPartData(pPart)), &pIL, + &ILLength); + + IFR(ValidateLoadModule(pIL, ILLength, pModule, Ctx, DiagStream, bLazyLoad)); + + HRESULT hr; + const DxilPartHeader *pDbgPart = nullptr; + if (FAILED(hr = FindDxilPart(pContainer, ContainerSize, + DFCC_ShaderDebugInfoDXIL, &pDbgPart)) && + hr != DXC_E_CONTAINER_MISSING_DXIL) { + return hr; + } + + if (pDbgPart) { + GetDxilProgramBitcode( + reinterpret_cast(GetDxilPartData(pDbgPart)), + &pIL, &ILLength); + if (FAILED(hr = ValidateLoadModule(pIL, ILLength, pDebugModule, DbgCtx, + DiagStream, bLazyLoad))) { + return hr; + } + } + + return S_OK; +} + +HRESULT ValidateLoadModuleFromContainer( + const void *pContainer, uint32_t ContainerSize, + std::unique_ptr &pModule, + std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, + llvm::LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream) { + return ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, + pDebugModule, Ctx, DbgCtx, DiagStream, + /*bLazyLoad*/ false); +} +// Lazy loads module from container, validating load, but not module. +HRESULT ValidateLoadModuleFromContainerLazy( + const void *pContainer, uint32_t ContainerSize, + std::unique_ptr &pModule, + std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, + llvm::LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream) { + return ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, + pDebugModule, Ctx, DbgCtx, DiagStream, + /*bLazyLoad*/ true); +} + +HRESULT ValidateDxilContainer(const void *pContainer, uint32_t ContainerSize, + llvm::Module *pDebugModule, + llvm::raw_ostream &DiagStream) { + LLVMContext Ctx, DbgCtx; + std::unique_ptr pModule, pDebugModuleInContainer; + + llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); + PrintDiagnosticContext DiagContext(DiagPrinter); + Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, + &DiagContext, true); + DbgCtx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, + &DiagContext, true); + + DiagRestore DR(pDebugModule, &DiagContext); + + IFR(ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, + pDebugModuleInContainer, Ctx, DbgCtx, + DiagStream)); + + if (pDebugModuleInContainer) + pDebugModule = pDebugModuleInContainer.get(); + + // Validate DXIL Module + IFR(ValidateDxilModule(pModule.get(), pDebugModule)); + + if (DiagContext.HasErrors() || DiagContext.HasWarnings()) { + return DXC_E_IR_VERIFICATION_FAILED; + } + + return ValidateDxilContainerParts( + pModule.get(), pDebugModule, + IsDxilContainerLike(pContainer, ContainerSize), ContainerSize); +} + +HRESULT ValidateDxilContainer(const void *pContainer, uint32_t ContainerSize, + llvm::raw_ostream &DiagStream) { + return ValidateDxilContainer(pContainer, ContainerSize, nullptr, DiagStream); +} +} // namespace hlsl diff --git a/lib/HLSL/DxilValidation.cpp b/lib/DxilValidation/DxilValidation.cpp similarity index 82% rename from lib/HLSL/DxilValidation.cpp rename to lib/DxilValidation/DxilValidation.cpp index 400d4731d6..957afb943b 100644 --- a/lib/HLSL/DxilValidation.cpp +++ b/lib/DxilValidation/DxilValidation.cpp @@ -9,7 +9,6 @@ // // /////////////////////////////////////////////////////////////////////////////// -#include "dxc/Support/FileIOHelper.h" #include "dxc/Support/Global.h" #include "dxc/Support/WinIncludes.h" @@ -22,20 +21,14 @@ #include "dxc/DXIL/DxilResourceProperties.h" #include "dxc/DXIL/DxilShaderModel.h" #include "dxc/DXIL/DxilUtil.h" -#include "dxc/DxilContainer/DxilContainer.h" -#include "dxc/DxilContainer/DxilContainerAssembler.h" -#include "dxc/DxilContainer/DxilPipelineStateValidation.h" -#include "dxc/DxilContainer/DxilRuntimeReflection.h" +#include "dxc/DxilValidation/DxilValidation.h" #include "dxc/HLSL/DxilGenerationPass.h" -#include "dxc/HLSL/DxilValidation.h" #include "llvm/Analysis/ReducibilityAnalysis.h" -#include "dxc/DxilRootSignature/DxilRootSignature.h" #include "dxc/HLSL/DxilPackSignatureElement.h" #include "dxc/HLSL/DxilSignatureAllocator.h" #include "dxc/HLSL/DxilSpanAllocator.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitVector.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" @@ -45,17 +38,16 @@ #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Dominators.h" -#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" -#include "llvm/IR/ModuleSlotTracker.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" #include "llvm/IR/Verifier.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" + +#include "DxilValidationUtils.h" + #include #include #include @@ -70,45 +62,6 @@ using std::vector; #include "DxilValidationImpl.inc" -namespace { - -// Utility class for setting and restoring the diagnostic context so we may -// capture errors/warnings -struct DiagRestore { - LLVMContext *Ctx = nullptr; - void *OrigDiagContext; - LLVMContext::DiagnosticHandlerTy OrigHandler; - - DiagRestore(llvm::LLVMContext &InputCtx, void *DiagContext) : Ctx(&InputCtx) { - init(DiagContext); - } - DiagRestore(Module *M, void *DiagContext) { - if (!M) - return; - Ctx = &M->getContext(); - init(DiagContext); - } - ~DiagRestore() { - if (!Ctx) - return; - Ctx->setDiagnosticHandler(OrigHandler, OrigDiagContext); - } - -private: - void init(void *DiagContext) { - OrigHandler = Ctx->getDiagnosticHandler(); - OrigDiagContext = Ctx->getDiagnosticContext(); - Ctx->setDiagnosticHandler( - hlsl::PrintDiagnosticContext::PrintDiagnosticHandler, DiagContext); - } -}; - -static void emitDxilDiag(LLVMContext &Ctx, const char *str) { - hlsl::dxilutil::EmitErrorOnContext(Ctx, str); -} - -} // namespace - namespace hlsl { // PrintDiagnosticContext methods. @@ -142,535 +95,6 @@ struct PSExecutionInfo { DXIL::SemanticKind OutputDepthKind = DXIL::SemanticKind::Invalid; const InterpolationMode *PositionInterpolationMode = nullptr; }; -// Save status like output write for entries. -struct EntryStatus { - bool hasOutputPosition[DXIL::kNumOutputStreams]; - unsigned OutputPositionMask[DXIL::kNumOutputStreams]; - std::vector outputCols; - std::vector patchConstOrPrimCols; - bool m_bCoverageIn, m_bInnerCoverageIn; - bool hasViewID; - unsigned domainLocSize; - EntryStatus(DxilEntryProps &entryProps) - : m_bCoverageIn(false), m_bInnerCoverageIn(false), hasViewID(false) { - for (unsigned i = 0; i < DXIL::kNumOutputStreams; i++) { - hasOutputPosition[i] = false; - OutputPositionMask[i] = 0; - } - - outputCols.resize(entryProps.sig.OutputSignature.GetElements().size(), 0); - patchConstOrPrimCols.resize( - entryProps.sig.PatchConstOrPrimSignature.GetElements().size(), 0); - } -}; - -struct ValidationContext { - bool Failed = false; - Module &M; - Module *pDebugModule; - DxilModule &DxilMod; - const Type *HandleTy; - const DataLayout &DL; - DebugLoc LastDebugLocEmit; - ValidationRule LastRuleEmit; - std::unordered_set entryFuncCallSet; - std::unordered_set patchConstFuncCallSet; - std::unordered_map UavCounterIncMap; - std::unordered_map HandleResIndexMap; - // TODO: save resource map for each createHandle/createHandleForLib. - std::unordered_map ResPropMap; - std::unordered_map> PatchConstantFuncMap; - std::unordered_map> entryStatusMap; - bool isLibProfile; - const unsigned kDxilControlFlowHintMDKind; - const unsigned kDxilPreciseMDKind; - const unsigned kDxilNonUniformMDKind; - const unsigned kLLVMLoopMDKind; - unsigned m_DxilMajor, m_DxilMinor; - ModuleSlotTracker slotTracker; - std::unique_ptr pCallGraph; - - ValidationContext(Module &llvmModule, Module *DebugModule, - DxilModule &dxilModule) - : M(llvmModule), pDebugModule(DebugModule), DxilMod(dxilModule), - DL(llvmModule.getDataLayout()), LastRuleEmit((ValidationRule)-1), - kDxilControlFlowHintMDKind(llvmModule.getContext().getMDKindID( - DxilMDHelper::kDxilControlFlowHintMDName)), - kDxilPreciseMDKind(llvmModule.getContext().getMDKindID( - DxilMDHelper::kDxilPreciseAttributeMDName)), - kDxilNonUniformMDKind(llvmModule.getContext().getMDKindID( - DxilMDHelper::kDxilNonUniformAttributeMDName)), - kLLVMLoopMDKind(llvmModule.getContext().getMDKindID("llvm.loop")), - slotTracker(&llvmModule, true) { - DxilMod.GetDxilVersion(m_DxilMajor, m_DxilMinor); - HandleTy = DxilMod.GetOP()->GetHandleType(); - - for (Function &F : llvmModule.functions()) { - if (DxilMod.HasDxilEntryProps(&F)) { - DxilEntryProps &entryProps = DxilMod.GetDxilEntryProps(&F); - entryStatusMap[&F] = llvm::make_unique(entryProps); - } - } - - isLibProfile = dxilModule.GetShaderModel()->IsLib(); - BuildResMap(); - // Collect patch constant map. - if (isLibProfile) { - for (Function &F : dxilModule.GetModule()->functions()) { - if (dxilModule.HasDxilEntryProps(&F)) { - DxilEntryProps &entryProps = dxilModule.GetDxilEntryProps(&F); - DxilFunctionProps &props = entryProps.props; - if (props.IsHS()) { - PatchConstantFuncMap[props.ShaderProps.HS.patchConstantFunc] - .emplace_back(&F); - } - } - } - } else { - Function *Entry = dxilModule.GetEntryFunction(); - if (!dxilModule.HasDxilEntryProps(Entry)) { - // must have props. - EmitFnError(Entry, ValidationRule::MetaNoEntryPropsForEntry); - return; - } - DxilEntryProps &entryProps = dxilModule.GetDxilEntryProps(Entry); - DxilFunctionProps &props = entryProps.props; - if (props.IsHS()) { - PatchConstantFuncMap[props.ShaderProps.HS.patchConstantFunc] - .emplace_back(Entry); - } - } - } - - void PropagateResMap(Value *V, DxilResourceBase *Res) { - auto it = ResPropMap.find(V); - if (it != ResPropMap.end()) { - DxilResourceProperties RP = - resource_helper::loadPropsFromResourceBase(Res); - DxilResourceProperties itRP = it->second; - if (itRP != RP) { - EmitResourceError(Res, ValidationRule::InstrResourceMapToSingleEntry); - } - } else { - DxilResourceProperties RP = - resource_helper::loadPropsFromResourceBase(Res); - ResPropMap[V] = RP; - for (User *U : V->users()) { - if (isa(U)) { - PropagateResMap(U, Res); - } else if (CallInst *CI = dyn_cast(U)) { - // Stop propagate on function call. - DxilInst_CreateHandleForLib hdl(CI); - if (hdl) { - DxilResourceProperties RP = - resource_helper::loadPropsFromResourceBase(Res); - ResPropMap[CI] = RP; - } - } else if (isa(U)) { - PropagateResMap(U, Res); - } else if (isa(U) && U->user_empty()) { - // For hlsl type. - continue; - } else { - EmitResourceError(Res, ValidationRule::InstrResourceUser); - } - } - } - } - - void BuildResMap() { - hlsl::OP *hlslOP = DxilMod.GetOP(); - - if (isLibProfile) { - std::unordered_set ResSet; - // Start from all global variable in resTab. - for (auto &Res : DxilMod.GetCBuffers()) - PropagateResMap(Res->GetGlobalSymbol(), Res.get()); - for (auto &Res : DxilMod.GetUAVs()) - PropagateResMap(Res->GetGlobalSymbol(), Res.get()); - for (auto &Res : DxilMod.GetSRVs()) - PropagateResMap(Res->GetGlobalSymbol(), Res.get()); - for (auto &Res : DxilMod.GetSamplers()) - PropagateResMap(Res->GetGlobalSymbol(), Res.get()); - } else { - // Scan all createHandle. - for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::CreateHandle)) { - Function *F = it.second; - if (!F) - continue; - for (User *U : F->users()) { - CallInst *CI = cast(U); - DxilInst_CreateHandle hdl(CI); - // Validate Class/RangeID/Index. - Value *resClass = hdl.get_resourceClass(); - if (!isa(resClass)) { - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - Value *rangeIndex = hdl.get_rangeId(); - if (!isa(rangeIndex)) { - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - - DxilResourceBase *Res = nullptr; - unsigned rangeId = hdl.get_rangeId_val(); - switch ( - static_cast(hdl.get_resourceClass_val())) { - default: - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - break; - case DXIL::ResourceClass::CBuffer: - if (DxilMod.GetCBuffers().size() > rangeId) { - Res = &DxilMod.GetCBuffer(rangeId); - } else { - // Emit Error. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - break; - case DXIL::ResourceClass::Sampler: - if (DxilMod.GetSamplers().size() > rangeId) { - Res = &DxilMod.GetSampler(rangeId); - } else { - // Emit Error. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - break; - case DXIL::ResourceClass::SRV: - if (DxilMod.GetSRVs().size() > rangeId) { - Res = &DxilMod.GetSRV(rangeId); - } else { - // Emit Error. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - break; - case DXIL::ResourceClass::UAV: - if (DxilMod.GetUAVs().size() > rangeId) { - Res = &DxilMod.GetUAV(rangeId); - } else { - // Emit Error. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - break; - } - - ConstantInt *cIndex = dyn_cast(hdl.get_index()); - if (!Res->GetHLSLType()->getPointerElementType()->isArrayTy()) { - if (!cIndex) { - // index must be 0 for none array resource. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - } - if (cIndex) { - unsigned index = cIndex->getLimitedValue(); - if (index < Res->GetLowerBound() || index > Res->GetUpperBound()) { - // index out of range. - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - } - HandleResIndexMap[CI] = rangeId; - DxilResourceProperties RP = - resource_helper::loadPropsFromResourceBase(Res); - ResPropMap[CI] = RP; - } - } - } - const ShaderModel &SM = *DxilMod.GetShaderModel(); - - for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::AnnotateHandle)) { - Function *F = it.second; - if (!F) - continue; - - for (User *U : F->users()) { - CallInst *CI = cast(U); - DxilInst_AnnotateHandle hdl(CI); - DxilResourceProperties RP = - resource_helper::loadPropsFromAnnotateHandle(hdl, SM); - if (RP.getResourceKind() == DXIL::ResourceKind::Invalid) { - EmitInstrError(CI, ValidationRule::InstrOpConstRange); - continue; - } - - ResPropMap[CI] = RP; - } - } - } - - bool HasEntryStatus(Function *F) { - return entryStatusMap.find(F) != entryStatusMap.end(); - } - - EntryStatus &GetEntryStatus(Function *F) { return *entryStatusMap[F]; } - - CallGraph &GetCallGraph() { - if (!pCallGraph) - pCallGraph = llvm::make_unique(M); - return *pCallGraph.get(); - } - - DxilResourceProperties GetResourceFromVal(Value *resVal); - - void EmitGlobalVariableFormatError(GlobalVariable *GV, ValidationRule rule, - ArrayRef args) { - std::string ruleText = GetValidationRuleText(rule); - FormatRuleText(ruleText, args); - if (pDebugModule) - GV = pDebugModule->getGlobalVariable(GV->getName()); - dxilutil::EmitErrorOnGlobalVariable(M.getContext(), GV, ruleText); - Failed = true; - } - - // This is the least desirable mechanism, as it has no context. - void EmitError(ValidationRule rule) { - dxilutil::EmitErrorOnContext(M.getContext(), GetValidationRuleText(rule)); - Failed = true; - } - - void FormatRuleText(std::string &ruleText, ArrayRef args) { - std::string escapedArg; - // Consider changing const char * to StringRef - for (unsigned i = 0; i < args.size(); i++) { - std::string argIdx = "%" + std::to_string(i); - StringRef pArg = args[i]; - if (pArg == "") - pArg = ""; - if (pArg[0] == 1) { - escapedArg = ""; - raw_string_ostream os(escapedArg); - dxilutil::PrintEscapedString(pArg, os); - os.flush(); - pArg = escapedArg; - } - - std::string::size_type offset = ruleText.find(argIdx); - if (offset == std::string::npos) - continue; - - unsigned size = argIdx.size(); - ruleText.replace(offset, size, pArg); - } - } - - void EmitFormatError(ValidationRule rule, ArrayRef args) { - std::string ruleText = GetValidationRuleText(rule); - FormatRuleText(ruleText, args); - dxilutil::EmitErrorOnContext(M.getContext(), ruleText); - Failed = true; - } - - void EmitMetaError(Metadata *Meta, ValidationRule rule) { - std::string O; - raw_string_ostream OSS(O); - Meta->print(OSS, &M); - dxilutil::EmitErrorOnContext(M.getContext(), - GetValidationRuleText(rule) + O); - Failed = true; - } - - // Use this instead of DxilResourceBase::GetGlobalName - std::string GetResourceName(const hlsl::DxilResourceBase *Res) { - if (!Res) - return "nullptr"; - std::string resName = Res->GetGlobalName(); - if (!resName.empty()) - return resName; - if (pDebugModule) { - DxilModule &DM = pDebugModule->GetOrCreateDxilModule(); - switch (Res->GetClass()) { - case DXIL::ResourceClass::CBuffer: - return DM.GetCBuffer(Res->GetID()).GetGlobalName(); - case DXIL::ResourceClass::Sampler: - return DM.GetSampler(Res->GetID()).GetGlobalName(); - case DXIL::ResourceClass::SRV: - return DM.GetSRV(Res->GetID()).GetGlobalName(); - case DXIL::ResourceClass::UAV: - return DM.GetUAV(Res->GetID()).GetGlobalName(); - default: - return "Invalid Resource"; - } - } - // When names have been stripped, use class and binding location to - // identify the resource. Format is roughly: - // Allocated: (CB|T|U|S): ((cb|t|u|s)[] - // space) Unallocated: (CB|T|U|S): (no bind - // location) Example: U0: TypedBuffer (u5[2] space1) - // [] and space skipped if 1 and 0 respectively. - return (Twine(Res->GetResIDPrefix()) + Twine(Res->GetID()) + ": " + - Twine(Res->GetResKindName()) + - (Res->IsAllocated() ? (" (" + Twine(Res->GetResBindPrefix()) + - Twine(Res->GetLowerBound()) + - (Res->IsUnbounded() ? Twine("[unbounded]") - : (Res->GetRangeSize() != 1) - ? "[" + Twine(Res->GetRangeSize()) + "]" - : Twine()) + - ((Res->GetSpaceID() != 0) - ? " space" + Twine(Res->GetSpaceID()) - : Twine()) + - ")") - : Twine(" (no bind location)"))) - .str(); - } - - void EmitResourceError(const hlsl::DxilResourceBase *Res, - ValidationRule rule) { - std::string QuotedRes = " '" + GetResourceName(Res) + "'"; - dxilutil::EmitErrorOnContext(M.getContext(), - GetValidationRuleText(rule) + QuotedRes); - Failed = true; - } - - void EmitResourceFormatError(const hlsl::DxilResourceBase *Res, - ValidationRule rule, ArrayRef args) { - std::string QuotedRes = " '" + GetResourceName(Res) + "'"; - std::string ruleText = GetValidationRuleText(rule); - FormatRuleText(ruleText, args); - dxilutil::EmitErrorOnContext(M.getContext(), ruleText + QuotedRes); - Failed = true; - } - - bool IsDebugFunctionCall(Instruction *I) { return isa(I); } - - Instruction *GetDebugInstr(Instruction *I) { - DXASSERT_NOMSG(I); - if (pDebugModule) { - // Look up the matching instruction in the debug module. - llvm::Function *Fn = I->getParent()->getParent(); - llvm::Function *DbgFn = pDebugModule->getFunction(Fn->getName()); - if (DbgFn) { - // Linear lookup, but then again, failing validation is rare. - inst_iterator it = inst_begin(Fn); - inst_iterator dbg_it = inst_begin(DbgFn); - while (IsDebugFunctionCall(&*dbg_it)) - ++dbg_it; - while (&*it != I) { - ++it; - ++dbg_it; - while (IsDebugFunctionCall(&*dbg_it)) - ++dbg_it; - } - return &*dbg_it; - } - } - return I; - } - - // Emit Error or note on instruction `I` with `Msg`. - // If `isError` is true, `Rule` may omit repeated errors - void EmitInstrDiagMsg(Instruction *I, ValidationRule Rule, std::string Msg, - bool isError = true) { - BasicBlock *BB = I->getParent(); - Function *F = BB->getParent(); - - Instruction *DbgI = GetDebugInstr(I); - if (isError) { - if (const DebugLoc L = DbgI->getDebugLoc()) { - // Instructions that get scalarized will likely hit - // this case. Avoid redundant diagnostic messages. - if (Rule == LastRuleEmit && L == LastDebugLocEmit) { - return; - } - LastRuleEmit = Rule; - LastDebugLocEmit = L; - } - dxilutil::EmitErrorOnInstruction(DbgI, Msg); - } else { - dxilutil::EmitNoteOnContext(DbgI->getContext(), Msg); - } - - // Add llvm information as a note to instruction string - std::string InstrStr; - raw_string_ostream InstrStream(InstrStr); - I->print(InstrStream, slotTracker); - InstrStream.flush(); - StringRef InstrStrRef = InstrStr; - InstrStrRef = InstrStrRef.ltrim(); // Ignore indentation - Msg = "at '" + InstrStrRef.str() + "'"; - - // Print the parent block name - Msg += " in block '"; - if (!BB->getName().empty()) { - Msg += BB->getName(); - } else { - unsigned idx = 0; - for (auto i = F->getBasicBlockList().begin(), - e = F->getBasicBlockList().end(); - i != e; ++i) { - if (BB == &(*i)) { - break; - } - idx++; - } - Msg += "#" + std::to_string(idx); - } - Msg += "'"; - - // Print the function name - Msg += " of function '" + F->getName().str() + "'."; - - dxilutil::EmitNoteOnContext(DbgI->getContext(), Msg); - - Failed = true; - } - - void EmitInstrError(Instruction *I, ValidationRule rule) { - EmitInstrDiagMsg(I, rule, GetValidationRuleText(rule)); - } - - void EmitInstrNote(Instruction *I, std::string Msg) { - EmitInstrDiagMsg(I, LastRuleEmit, Msg, false); - } - - void EmitInstrFormatError(Instruction *I, ValidationRule rule, - ArrayRef args) { - std::string ruleText = GetValidationRuleText(rule); - FormatRuleText(ruleText, args); - EmitInstrDiagMsg(I, rule, ruleText); - } - - void EmitSignatureError(DxilSignatureElement *SE, ValidationRule rule) { - EmitFormatError(rule, {SE->GetName()}); - } - - void EmitTypeError(Type *Ty, ValidationRule rule) { - std::string O; - raw_string_ostream OSS(O); - Ty->print(OSS); - EmitFormatError(rule, {OSS.str()}); - } - - void EmitFnError(Function *F, ValidationRule rule) { - if (pDebugModule) - if (Function *dbgF = pDebugModule->getFunction(F->getName())) - F = dbgF; - dxilutil::EmitErrorOnFunction(M.getContext(), F, - GetValidationRuleText(rule)); - Failed = true; - } - - void EmitFnFormatError(Function *F, ValidationRule rule, - ArrayRef args) { - std::string ruleText = GetValidationRuleText(rule); - FormatRuleText(ruleText, args); - if (pDebugModule) - if (Function *dbgF = pDebugModule->getFunction(F->getName())) - F = dbgF; - dxilutil::EmitErrorOnFunction(M.getContext(), F, ruleText); - Failed = true; - } - - void EmitFnAttributeError(Function *F, StringRef Kind, StringRef Value) { - EmitFnFormatError(F, ValidationRule::DeclFnAttribute, - {F->getName(), Kind, Value}); - } -}; static unsigned ValidateSignatureRowCol(Instruction *I, DxilSignatureElement &SE, Value *rowVal, @@ -6357,7 +5781,7 @@ static void ValidateUninitializedOutput(ValidationContext &ValCtx) { } } -HRESULT ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule) { +uint32_t ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule) { DxilModule *pDxilModule = DxilModule::TryGetDxilModule(pModule); if (!pDxilModule) { return DXC_E_IR_VERIFICATION_FAILED; @@ -6404,636 +5828,4 @@ HRESULT ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule) { return S_OK; } -// DXIL Container Verification Functions - -static void VerifyBlobPartMatches(ValidationContext &ValCtx, LPCSTR pName, - DxilPartWriter *pWriter, const void *pData, - uint32_t Size) { - if (!pData && pWriter->size()) { - // No blob part, but writer says non-zero size is expected. - ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, {pName}); - return; - } - - // Compare sizes - if (pWriter->size() != Size) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {pName}); - return; - } - - if (Size == 0) { - return; - } - - CComPtr pOutputStream; - IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pOutputStream)); - pOutputStream->Reserve(Size); - - pWriter->write(pOutputStream); - DXASSERT(pOutputStream->GetPtrSize() == Size, - "otherwise, DxilPartWriter misreported size"); - - if (memcmp(pData, pOutputStream->GetPtr(), Size)) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {pName}); - return; - } - - return; -} - -static void VerifySignatureMatches(ValidationContext &ValCtx, - DXIL::SignatureKind SigKind, - const void *pSigData, uint32_t SigSize) { - // Generate corresponding signature from module and memcmp - - const char *pName = nullptr; - switch (SigKind) { - case hlsl::DXIL::SignatureKind::Input: - pName = "Program Input Signature"; - break; - case hlsl::DXIL::SignatureKind::Output: - pName = "Program Output Signature"; - break; - case hlsl::DXIL::SignatureKind::PatchConstOrPrim: - if (ValCtx.DxilMod.GetShaderModel()->GetKind() == DXIL::ShaderKind::Mesh) - pName = "Program Primitive Signature"; - else - pName = "Program Patch Constant Signature"; - break; - default: - break; - } - - unique_ptr pWriter( - NewProgramSignatureWriter(ValCtx.DxilMod, SigKind)); - VerifyBlobPartMatches(ValCtx, pName, pWriter.get(), pSigData, SigSize); -} - -bool VerifySignatureMatches(llvm::Module *pModule, DXIL::SignatureKind SigKind, - const void *pSigData, uint32_t SigSize) { - ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); - VerifySignatureMatches(ValCtx, SigKind, pSigData, SigSize); - return !ValCtx.Failed; -} - -static void VerifyPSVMatches(ValidationContext &ValCtx, const void *pPSVData, - uint32_t PSVSize) { - uint32_t PSVVersion = - MAX_PSV_VERSION; // This should be set to the newest version - unique_ptr pWriter(NewPSVWriter(ValCtx.DxilMod, PSVVersion)); - // Try each version in case an earlier version matches module - while (PSVVersion && pWriter->size() != PSVSize) { - PSVVersion--; - pWriter.reset(NewPSVWriter(ValCtx.DxilMod, PSVVersion)); - } - // generate PSV data from module and memcmp - VerifyBlobPartMatches(ValCtx, "Pipeline State Validation", pWriter.get(), - pPSVData, PSVSize); -} - -bool VerifyPSVMatches(llvm::Module *pModule, const void *pPSVData, - uint32_t PSVSize) { - ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); - VerifyPSVMatches(ValCtx, pPSVData, PSVSize); - return !ValCtx.Failed; -} - -static void VerifyFeatureInfoMatches(ValidationContext &ValCtx, - const void *pFeatureInfoData, - uint32_t FeatureInfoSize) { - // generate Feature Info data from module and memcmp - unique_ptr pWriter(NewFeatureInfoWriter(ValCtx.DxilMod)); - VerifyBlobPartMatches(ValCtx, "Feature Info", pWriter.get(), pFeatureInfoData, - FeatureInfoSize); -} - -// return true if the pBlob is a valid, well-formed CompilerVersion part, false -// otherwise -bool ValidateCompilerVersionPart(const void *pBlobPtr, UINT blobSize) { - // The hlsl::DxilCompilerVersion struct is always 16 bytes. (2 2-byte - // uint16's, 3 4-byte uint32's) The blob size should absolutely never be less - // than 16 bytes. - if (blobSize < sizeof(hlsl::DxilCompilerVersion)) { - return false; - } - - const hlsl::DxilCompilerVersion *pDCV = - (const hlsl::DxilCompilerVersion *)pBlobPtr; - if (pDCV->VersionStringListSizeInBytes == 0) { - // No version strings, just make sure there is no extra space. - return blobSize == sizeof(hlsl::DxilCompilerVersion); - } - - // after this point, we know VersionStringListSizeInBytes >= 1, because it is - // a UINT - - UINT EndOfVersionStringIndex = - sizeof(hlsl::DxilCompilerVersion) + pDCV->VersionStringListSizeInBytes; - // Make sure that the buffer size is large enough to contain both the DCV - // struct and the version string but not any larger than necessary - if (PSVALIGN4(EndOfVersionStringIndex) != blobSize) { - return false; - } - - const char *VersionStringsListData = - (const char *)pBlobPtr + sizeof(hlsl::DxilCompilerVersion); - UINT VersionStringListSizeInBytes = pDCV->VersionStringListSizeInBytes; - - // now make sure that any pad bytes that were added are null-terminators. - for (UINT i = VersionStringListSizeInBytes; - i < blobSize - sizeof(hlsl::DxilCompilerVersion); i++) { - if (VersionStringsListData[i] != '\0') { - return false; - } - } - - // Now, version string validation - // first, the final byte of the string should always be null-terminator so - // that the string ends - if (VersionStringsListData[VersionStringListSizeInBytes - 1] != '\0') { - return false; - } - - // construct the first string - // data format for VersionString can be see in the definition for the - // DxilCompilerVersion struct. summary: 2 strings that each end with the null - // terminator, and [0-3] null terminators after the final null terminator - StringRef firstStr(VersionStringsListData); - - // if the second string exists, attempt to construct it. - if (VersionStringListSizeInBytes > (firstStr.size() + 1)) { - StringRef secondStr(VersionStringsListData + firstStr.size() + 1); - - // the VersionStringListSizeInBytes member should be exactly equal to the - // two string lengths, plus the 2 null terminator bytes. - if (VersionStringListSizeInBytes != - firstStr.size() + secondStr.size() + 2) { - return false; - } - } else { - // the VersionStringListSizeInBytes member should be exactly equal to the - // first string length, plus the 1 null terminator byte. - if (VersionStringListSizeInBytes != firstStr.size() + 1) { - return false; - } - } - - return true; -} - -static void VerifyRDATMatches(ValidationContext &ValCtx, const void *pRDATData, - uint32_t RDATSize) { - const char *PartName = "Runtime Data (RDAT)"; - RDAT::DxilRuntimeData rdat(pRDATData, RDATSize); - if (!rdat.Validate()) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {PartName}); - return; - } - - // If DxilModule subobjects already loaded, validate these against the RDAT - // blob, otherwise, load subobject into DxilModule to generate reference RDAT. - if (!ValCtx.DxilMod.GetSubobjects()) { - auto table = rdat.GetSubobjectTable(); - if (table && table.Count() > 0) { - ValCtx.DxilMod.ResetSubobjects(new DxilSubobjects()); - if (!LoadSubobjectsFromRDAT(*ValCtx.DxilMod.GetSubobjects(), rdat)) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, - {PartName}); - return; - } - } - } - - unique_ptr pWriter(NewRDATWriter(ValCtx.DxilMod)); - VerifyBlobPartMatches(ValCtx, PartName, pWriter.get(), pRDATData, RDATSize); -} - -bool VerifyRDATMatches(llvm::Module *pModule, const void *pRDATData, - uint32_t RDATSize) { - ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); - VerifyRDATMatches(ValCtx, pRDATData, RDATSize); - return !ValCtx.Failed; -} - -bool VerifyFeatureInfoMatches(llvm::Module *pModule, - const void *pFeatureInfoData, - uint32_t FeatureInfoSize) { - ValidationContext ValCtx(*pModule, nullptr, pModule->GetOrCreateDxilModule()); - VerifyFeatureInfoMatches(ValCtx, pFeatureInfoData, FeatureInfoSize); - return !ValCtx.Failed; -} - -HRESULT ValidateDxilContainerParts(llvm::Module *pModule, - llvm::Module *pDebugModule, - const DxilContainerHeader *pContainer, - uint32_t ContainerSize) { - - DXASSERT_NOMSG(pModule); - if (!pContainer || !IsValidDxilContainer(pContainer, ContainerSize)) { - return DXC_E_CONTAINER_INVALID; - } - - DxilModule *pDxilModule = DxilModule::TryGetDxilModule(pModule); - if (!pDxilModule) { - return DXC_E_IR_VERIFICATION_FAILED; - } - - ValidationContext ValCtx(*pModule, pDebugModule, *pDxilModule); - - DXIL::ShaderKind ShaderKind = pDxilModule->GetShaderModel()->GetKind(); - bool bTessOrMesh = ShaderKind == DXIL::ShaderKind::Hull || - ShaderKind == DXIL::ShaderKind::Domain || - ShaderKind == DXIL::ShaderKind::Mesh; - - std::unordered_set FourCCFound; - const DxilPartHeader *pRootSignaturePart = nullptr; - const DxilPartHeader *pPSVPart = nullptr; - - for (auto it = begin(pContainer), itEnd = end(pContainer); it != itEnd; - ++it) { - const DxilPartHeader *pPart = *it; - - char szFourCC[5]; - PartKindToCharArray(pPart->PartFourCC, szFourCC); - if (FourCCFound.find(pPart->PartFourCC) != FourCCFound.end()) { - // Two parts with same FourCC found - ValCtx.EmitFormatError(ValidationRule::ContainerPartRepeated, {szFourCC}); - continue; - } - FourCCFound.insert(pPart->PartFourCC); - - switch (pPart->PartFourCC) { - case DFCC_InputSignature: - if (ValCtx.isLibProfile) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } else { - VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Input, - GetDxilPartData(pPart), pPart->PartSize); - } - break; - case DFCC_OutputSignature: - if (ValCtx.isLibProfile) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } else { - VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Output, - GetDxilPartData(pPart), pPart->PartSize); - } - break; - case DFCC_PatchConstantSignature: - if (ValCtx.isLibProfile) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } else { - if (bTessOrMesh) { - VerifySignatureMatches(ValCtx, DXIL::SignatureKind::PatchConstOrPrim, - GetDxilPartData(pPart), pPart->PartSize); - } else { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, - {"Program Patch Constant Signature"}); - } - } - break; - case DFCC_FeatureInfo: - VerifyFeatureInfoMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); - break; - case DFCC_CompilerVersion: - // This blob is either a PDB, or a library profile - if (ValCtx.isLibProfile) { - if (!ValidateCompilerVersionPart((void *)GetDxilPartData(pPart), - pPart->PartSize)) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } - } else { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } - break; - - case DFCC_RootSignature: - pRootSignaturePart = pPart; - if (ValCtx.isLibProfile) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } - break; - case DFCC_PipelineStateValidation: - pPSVPart = pPart; - if (ValCtx.isLibProfile) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } else { - VerifyPSVMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); - } - break; - - // Skip these - case DFCC_ResourceDef: - case DFCC_ShaderStatistics: - case DFCC_PrivateData: - case DFCC_DXIL: - case DFCC_ShaderDebugInfoDXIL: - case DFCC_ShaderDebugName: - continue; - - case DFCC_ShaderHash: - if (pPart->PartSize != sizeof(DxilShaderHash)) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } - break; - - // Runtime Data (RDAT) for libraries - case DFCC_RuntimeData: - if (ValCtx.isLibProfile) { - // TODO: validate without exact binary comparison of serialized data - // - support earlier versions - // - verify no newer record versions than known here (size no larger - // than newest version) - // - verify all data makes sense and matches expectations based on - // module - VerifyRDATMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize); - } else { - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, - {szFourCC}); - } - break; - - case DFCC_Container: - default: - ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, {szFourCC}); - break; - } - } - - // Verify required parts found - if (ValCtx.isLibProfile) { - if (FourCCFound.find(DFCC_RuntimeData) == FourCCFound.end()) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, - {"Runtime Data (RDAT)"}); - } - } else { - if (FourCCFound.find(DFCC_InputSignature) == FourCCFound.end()) { - VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Input, nullptr, 0); - } - if (FourCCFound.find(DFCC_OutputSignature) == FourCCFound.end()) { - VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Output, nullptr, 0); - } - if (bTessOrMesh && - FourCCFound.find(DFCC_PatchConstantSignature) == FourCCFound.end() && - pDxilModule->GetPatchConstOrPrimSignature().GetElements().size()) { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, - {"Program Patch Constant Signature"}); - } - if (FourCCFound.find(DFCC_FeatureInfo) == FourCCFound.end()) { - // Could be optional, but RS1 runtime doesn't handle this case properly. - ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, - {"Feature Info"}); - } - - // Validate Root Signature - if (pPSVPart) { - if (pRootSignaturePart) { - std::string diagStr; - raw_string_ostream DiagStream(diagStr); - try { - RootSignatureHandle RS; - RS.LoadSerialized( - (const uint8_t *)GetDxilPartData(pRootSignaturePart), - pRootSignaturePart->PartSize); - RS.Deserialize(); - IFTBOOL(VerifyRootSignatureWithShaderPSV( - RS.GetDesc(), pDxilModule->GetShaderModel()->GetKind(), - GetDxilPartData(pPSVPart), pPSVPart->PartSize, - DiagStream), - DXC_E_INCORRECT_ROOT_SIGNATURE); - } catch (...) { - ValCtx.EmitError(ValidationRule::ContainerRootSignatureIncompatible); - emitDxilDiag(pModule->getContext(), DiagStream.str().c_str()); - } - } - } else { - ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, - {"Pipeline State Validation"}); - } - } - - if (ValCtx.Failed) { - return DXC_E_MALFORMED_CONTAINER; - } - return S_OK; -} - -static HRESULT FindDxilPart(const void *pContainerBytes, uint32_t ContainerSize, - DxilFourCC FourCC, const DxilPartHeader **ppPart) { - - const DxilContainerHeader *pContainer = - IsDxilContainerLike(pContainerBytes, ContainerSize); - - if (!pContainer) { - IFR(DXC_E_CONTAINER_INVALID); - } - if (!IsValidDxilContainer(pContainer, ContainerSize)) { - IFR(DXC_E_CONTAINER_INVALID); - } - - DxilPartIterator it = - std::find_if(begin(pContainer), end(pContainer), DxilPartIsType(FourCC)); - if (it == end(pContainer)) { - IFR(DXC_E_CONTAINER_MISSING_DXIL); - } - - const DxilProgramHeader *pProgramHeader = - reinterpret_cast(GetDxilPartData(*it)); - if (!IsValidDxilProgramHeader(pProgramHeader, (*it)->PartSize)) { - IFR(DXC_E_CONTAINER_INVALID); - } - - *ppPart = *it; - return S_OK; -} - -HRESULT ValidateLoadModule(const char *pIL, uint32_t ILLength, - unique_ptr &pModule, LLVMContext &Ctx, - llvm::raw_ostream &DiagStream, unsigned bLazyLoad) { - - llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); - PrintDiagnosticContext DiagContext(DiagPrinter); - DiagRestore DR(Ctx, &DiagContext); - - std::unique_ptr pBitcodeBuf; - pBitcodeBuf.reset(llvm::MemoryBuffer::getMemBuffer( - llvm::StringRef(pIL, ILLength), "", false) - .release()); - - ErrorOr> loadedModuleResult = - bLazyLoad == 0 - ? llvm::parseBitcodeFile(pBitcodeBuf->getMemBufferRef(), Ctx, nullptr, - true /*Track Bitstream*/) - : llvm::getLazyBitcodeModule(std::move(pBitcodeBuf), Ctx, nullptr, - false, true /*Track Bitstream*/); - - // DXIL disallows some LLVM bitcode constructs, like unaccounted-for - // sub-blocks. These appear as warnings, which the validator should reject. - if (DiagContext.HasErrors() || DiagContext.HasWarnings() || - loadedModuleResult.getError()) - return DXC_E_IR_VERIFICATION_FAILED; - - pModule = std::move(loadedModuleResult.get()); - return S_OK; -} - -HRESULT ValidateDxilBitcode(const char *pIL, uint32_t ILLength, - llvm::raw_ostream &DiagStream) { - - LLVMContext Ctx; - std::unique_ptr pModule; - - llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); - PrintDiagnosticContext DiagContext(DiagPrinter); - Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, - &DiagContext, true); - - HRESULT hr; - if (FAILED(hr = ValidateLoadModule(pIL, ILLength, pModule, Ctx, DiagStream, - /*bLazyLoad*/ false))) - return hr; - - if (FAILED(hr = ValidateDxilModule(pModule.get(), nullptr))) - return hr; - - DxilModule &dxilModule = pModule->GetDxilModule(); - auto &SerializedRootSig = dxilModule.GetSerializedRootSignature(); - if (!SerializedRootSig.empty()) { - unique_ptr pWriter(NewPSVWriter(dxilModule)); - DXASSERT_NOMSG(pWriter->size()); - CComPtr pOutputStream; - IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pOutputStream)); - pOutputStream->Reserve(pWriter->size()); - pWriter->write(pOutputStream); - DxilVersionedRootSignature desc; - try { - DeserializeRootSignature(SerializedRootSig.data(), - SerializedRootSig.size(), desc.get_address_of()); - if (!desc.get()) { - return DXC_E_INCORRECT_ROOT_SIGNATURE; - } - IFTBOOL(VerifyRootSignatureWithShaderPSV( - desc.get(), dxilModule.GetShaderModel()->GetKind(), - pOutputStream->GetPtr(), pWriter->size(), DiagStream), - DXC_E_INCORRECT_ROOT_SIGNATURE); - } catch (...) { - return DXC_E_INCORRECT_ROOT_SIGNATURE; - } - } - - if (DiagContext.HasErrors() || DiagContext.HasWarnings()) { - return DXC_E_IR_VERIFICATION_FAILED; - } - - return S_OK; -} - -static HRESULT ValidateLoadModuleFromContainer( - const void *pContainer, uint32_t ContainerSize, - std::unique_ptr &pModule, - std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, - LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream, unsigned bLazyLoad) { - llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); - PrintDiagnosticContext DiagContext(DiagPrinter); - DiagRestore DR(Ctx, &DiagContext); - DiagRestore DR2(DbgCtx, &DiagContext); - - const DxilPartHeader *pPart = nullptr; - IFR(FindDxilPart(pContainer, ContainerSize, DFCC_DXIL, &pPart)); - - const char *pIL = nullptr; - uint32_t ILLength = 0; - GetDxilProgramBitcode( - reinterpret_cast(GetDxilPartData(pPart)), &pIL, - &ILLength); - - IFR(ValidateLoadModule(pIL, ILLength, pModule, Ctx, DiagStream, bLazyLoad)); - - HRESULT hr; - const DxilPartHeader *pDbgPart = nullptr; - if (FAILED(hr = FindDxilPart(pContainer, ContainerSize, - DFCC_ShaderDebugInfoDXIL, &pDbgPart)) && - hr != DXC_E_CONTAINER_MISSING_DXIL) { - return hr; - } - - if (pDbgPart) { - GetDxilProgramBitcode( - reinterpret_cast(GetDxilPartData(pDbgPart)), - &pIL, &ILLength); - if (FAILED(hr = ValidateLoadModule(pIL, ILLength, pDebugModule, DbgCtx, - DiagStream, bLazyLoad))) { - return hr; - } - } - - return S_OK; -} - -HRESULT ValidateLoadModuleFromContainer( - const void *pContainer, uint32_t ContainerSize, - std::unique_ptr &pModule, - std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, - llvm::LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream) { - return ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, - pDebugModule, Ctx, DbgCtx, DiagStream, - /*bLazyLoad*/ false); -} -// Lazy loads module from container, validating load, but not module. -HRESULT ValidateLoadModuleFromContainerLazy( - const void *pContainer, uint32_t ContainerSize, - std::unique_ptr &pModule, - std::unique_ptr &pDebugModule, llvm::LLVMContext &Ctx, - llvm::LLVMContext &DbgCtx, llvm::raw_ostream &DiagStream) { - return ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, - pDebugModule, Ctx, DbgCtx, DiagStream, - /*bLazyLoad*/ true); -} - -HRESULT ValidateDxilContainer(const void *pContainer, uint32_t ContainerSize, - llvm::Module *pDebugModule, - llvm::raw_ostream &DiagStream) { - LLVMContext Ctx, DbgCtx; - std::unique_ptr pModule, pDebugModuleInContainer; - - llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream); - PrintDiagnosticContext DiagContext(DiagPrinter); - Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, - &DiagContext, true); - DbgCtx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler, - &DiagContext, true); - - DiagRestore DR(pDebugModule, &DiagContext); - - IFR(ValidateLoadModuleFromContainer(pContainer, ContainerSize, pModule, - pDebugModuleInContainer, Ctx, DbgCtx, - DiagStream)); - - if (pDebugModuleInContainer) - pDebugModule = pDebugModuleInContainer.get(); - - // Validate DXIL Module - IFR(ValidateDxilModule(pModule.get(), pDebugModule)); - - if (DiagContext.HasErrors() || DiagContext.HasWarnings()) { - return DXC_E_IR_VERIFICATION_FAILED; - } - - return ValidateDxilContainerParts( - pModule.get(), pDebugModule, - IsDxilContainerLike(pContainer, ContainerSize), ContainerSize); -} - -HRESULT ValidateDxilContainer(const void *pContainer, uint32_t ContainerSize, - llvm::raw_ostream &DiagStream) { - return ValidateDxilContainer(pContainer, ContainerSize, nullptr, DiagStream); -} } // namespace hlsl diff --git a/lib/DxilValidation/DxilValidationUtils.cpp b/lib/DxilValidation/DxilValidationUtils.cpp new file mode 100644 index 0000000000..f0f85fcd26 --- /dev/null +++ b/lib/DxilValidation/DxilValidationUtils.cpp @@ -0,0 +1,526 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// DxilValidationUttils.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. // +// // +// This file provides utils for validating DXIL. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "DxilValidationUtils.h" + +#include "dxc/DXIL/DxilEntryProps.h" +#include "dxc/DXIL/DxilInstructions.h" +#include "dxc/DXIL/DxilModule.h" +#include "dxc/DXIL/DxilOperations.h" +#include "dxc/DXIL/DxilUtil.h" +#include "dxc/Support/Global.h" + +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Support/raw_ostream.h" + +namespace hlsl { +EntryStatus::EntryStatus(DxilEntryProps &entryProps) + : m_bCoverageIn(false), m_bInnerCoverageIn(false), hasViewID(false) { + for (unsigned i = 0; i < DXIL::kNumOutputStreams; i++) { + hasOutputPosition[i] = false; + OutputPositionMask[i] = 0; + } + + outputCols.resize(entryProps.sig.OutputSignature.GetElements().size(), 0); + patchConstOrPrimCols.resize( + entryProps.sig.PatchConstOrPrimSignature.GetElements().size(), 0); +} + +ValidationContext::ValidationContext(Module &llvmModule, Module *DebugModule, + DxilModule &dxilModule) + : M(llvmModule), pDebugModule(DebugModule), DxilMod(dxilModule), + DL(llvmModule.getDataLayout()), LastRuleEmit((ValidationRule)-1), + kDxilControlFlowHintMDKind(llvmModule.getContext().getMDKindID( + DxilMDHelper::kDxilControlFlowHintMDName)), + kDxilPreciseMDKind(llvmModule.getContext().getMDKindID( + DxilMDHelper::kDxilPreciseAttributeMDName)), + kDxilNonUniformMDKind(llvmModule.getContext().getMDKindID( + DxilMDHelper::kDxilNonUniformAttributeMDName)), + kLLVMLoopMDKind(llvmModule.getContext().getMDKindID("llvm.loop")), + slotTracker(&llvmModule, true) { + DxilMod.GetDxilVersion(m_DxilMajor, m_DxilMinor); + HandleTy = DxilMod.GetOP()->GetHandleType(); + + for (Function &F : llvmModule.functions()) { + if (DxilMod.HasDxilEntryProps(&F)) { + DxilEntryProps &entryProps = DxilMod.GetDxilEntryProps(&F); + entryStatusMap[&F] = llvm::make_unique(entryProps); + } + } + + isLibProfile = dxilModule.GetShaderModel()->IsLib(); + BuildResMap(); + // Collect patch constant map. + if (isLibProfile) { + for (Function &F : dxilModule.GetModule()->functions()) { + if (dxilModule.HasDxilEntryProps(&F)) { + DxilEntryProps &entryProps = dxilModule.GetDxilEntryProps(&F); + DxilFunctionProps &props = entryProps.props; + if (props.IsHS()) { + PatchConstantFuncMap[props.ShaderProps.HS.patchConstantFunc] + .emplace_back(&F); + } + } + } + } else { + Function *Entry = dxilModule.GetEntryFunction(); + if (!dxilModule.HasDxilEntryProps(Entry)) { + // must have props. + EmitFnError(Entry, ValidationRule::MetaNoEntryPropsForEntry); + return; + } + DxilEntryProps &entryProps = dxilModule.GetDxilEntryProps(Entry); + DxilFunctionProps &props = entryProps.props; + if (props.IsHS()) { + PatchConstantFuncMap[props.ShaderProps.HS.patchConstantFunc].emplace_back( + Entry); + } + } +} + +void ValidationContext::PropagateResMap(Value *V, DxilResourceBase *Res) { + auto it = ResPropMap.find(V); + if (it != ResPropMap.end()) { + DxilResourceProperties RP = resource_helper::loadPropsFromResourceBase(Res); + DxilResourceProperties itRP = it->second; + if (itRP != RP) { + EmitResourceError(Res, ValidationRule::InstrResourceMapToSingleEntry); + } + } else { + DxilResourceProperties RP = resource_helper::loadPropsFromResourceBase(Res); + ResPropMap[V] = RP; + for (User *U : V->users()) { + if (isa(U)) { + PropagateResMap(U, Res); + } else if (CallInst *CI = dyn_cast(U)) { + // Stop propagate on function call. + DxilInst_CreateHandleForLib hdl(CI); + if (hdl) { + DxilResourceProperties RP = + resource_helper::loadPropsFromResourceBase(Res); + ResPropMap[CI] = RP; + } + } else if (isa(U)) { + PropagateResMap(U, Res); + } else if (isa(U) && U->user_empty()) { + // For hlsl type. + continue; + } else { + EmitResourceError(Res, ValidationRule::InstrResourceUser); + } + } + } +} + +void ValidationContext::BuildResMap() { + hlsl::OP *hlslOP = DxilMod.GetOP(); + + if (isLibProfile) { + std::unordered_set ResSet; + // Start from all global variable in resTab. + for (auto &Res : DxilMod.GetCBuffers()) + PropagateResMap(Res->GetGlobalSymbol(), Res.get()); + for (auto &Res : DxilMod.GetUAVs()) + PropagateResMap(Res->GetGlobalSymbol(), Res.get()); + for (auto &Res : DxilMod.GetSRVs()) + PropagateResMap(Res->GetGlobalSymbol(), Res.get()); + for (auto &Res : DxilMod.GetSamplers()) + PropagateResMap(Res->GetGlobalSymbol(), Res.get()); + } else { + // Scan all createHandle. + for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::CreateHandle)) { + Function *F = it.second; + if (!F) + continue; + for (User *U : F->users()) { + CallInst *CI = cast(U); + DxilInst_CreateHandle hdl(CI); + // Validate Class/RangeID/Index. + Value *resClass = hdl.get_resourceClass(); + if (!isa(resClass)) { + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + Value *rangeIndex = hdl.get_rangeId(); + if (!isa(rangeIndex)) { + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + + DxilResourceBase *Res = nullptr; + unsigned rangeId = hdl.get_rangeId_val(); + switch (static_cast(hdl.get_resourceClass_val())) { + default: + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + break; + case DXIL::ResourceClass::CBuffer: + if (DxilMod.GetCBuffers().size() > rangeId) { + Res = &DxilMod.GetCBuffer(rangeId); + } else { + // Emit Error. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + break; + case DXIL::ResourceClass::Sampler: + if (DxilMod.GetSamplers().size() > rangeId) { + Res = &DxilMod.GetSampler(rangeId); + } else { + // Emit Error. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + break; + case DXIL::ResourceClass::SRV: + if (DxilMod.GetSRVs().size() > rangeId) { + Res = &DxilMod.GetSRV(rangeId); + } else { + // Emit Error. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + break; + case DXIL::ResourceClass::UAV: + if (DxilMod.GetUAVs().size() > rangeId) { + Res = &DxilMod.GetUAV(rangeId); + } else { + // Emit Error. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + break; + } + + ConstantInt *cIndex = dyn_cast(hdl.get_index()); + if (!Res->GetHLSLType()->getPointerElementType()->isArrayTy()) { + if (!cIndex) { + // index must be 0 for none array resource. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + } + if (cIndex) { + unsigned index = cIndex->getLimitedValue(); + if (index < Res->GetLowerBound() || index > Res->GetUpperBound()) { + // index out of range. + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + } + HandleResIndexMap[CI] = rangeId; + DxilResourceProperties RP = + resource_helper::loadPropsFromResourceBase(Res); + ResPropMap[CI] = RP; + } + } + } + const ShaderModel &SM = *DxilMod.GetShaderModel(); + + for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::AnnotateHandle)) { + Function *F = it.second; + if (!F) + continue; + + for (User *U : F->users()) { + CallInst *CI = cast(U); + DxilInst_AnnotateHandle hdl(CI); + DxilResourceProperties RP = + resource_helper::loadPropsFromAnnotateHandle(hdl, SM); + if (RP.getResourceKind() == DXIL::ResourceKind::Invalid) { + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + + ResPropMap[CI] = RP; + } + } +} + +bool ValidationContext::HasEntryStatus(Function *F) { + return entryStatusMap.find(F) != entryStatusMap.end(); +} + +EntryStatus &ValidationContext::GetEntryStatus(Function *F) { + return *entryStatusMap[F]; +} + +CallGraph &ValidationContext::GetCallGraph() { + if (!pCallGraph) + pCallGraph = llvm::make_unique(M); + return *pCallGraph.get(); +} + +void ValidationContext::EmitGlobalVariableFormatError( + GlobalVariable *GV, ValidationRule rule, ArrayRef args) { + std::string ruleText = GetValidationRuleText(rule); + FormatRuleText(ruleText, args); + if (pDebugModule) + GV = pDebugModule->getGlobalVariable(GV->getName()); + dxilutil::EmitErrorOnGlobalVariable(M.getContext(), GV, ruleText); + Failed = true; +} + +// This is the least desirable mechanism, as it has no context. +void ValidationContext::EmitError(ValidationRule rule) { + dxilutil::EmitErrorOnContext(M.getContext(), GetValidationRuleText(rule)); + Failed = true; +} + +void ValidationContext::FormatRuleText(std::string &ruleText, + ArrayRef args) { + std::string escapedArg; + // Consider changing const char * to StringRef + for (unsigned i = 0; i < args.size(); i++) { + std::string argIdx = "%" + std::to_string(i); + StringRef pArg = args[i]; + if (pArg == "") + pArg = ""; + if (pArg[0] == 1) { + escapedArg = ""; + raw_string_ostream os(escapedArg); + dxilutil::PrintEscapedString(pArg, os); + os.flush(); + pArg = escapedArg; + } + + std::string::size_type offset = ruleText.find(argIdx); + if (offset == std::string::npos) + continue; + + unsigned size = argIdx.size(); + ruleText.replace(offset, size, pArg); + } +} + +void ValidationContext::EmitFormatError(ValidationRule rule, + ArrayRef args) { + std::string ruleText = GetValidationRuleText(rule); + FormatRuleText(ruleText, args); + dxilutil::EmitErrorOnContext(M.getContext(), ruleText); + Failed = true; +} + +void ValidationContext::EmitMetaError(Metadata *Meta, ValidationRule rule) { + std::string O; + raw_string_ostream OSS(O); + Meta->print(OSS, &M); + dxilutil::EmitErrorOnContext(M.getContext(), GetValidationRuleText(rule) + O); + Failed = true; +} + +// Use this instead of DxilResourceBase::GetGlobalName +std::string +ValidationContext::GetResourceName(const hlsl::DxilResourceBase *Res) { + if (!Res) + return "nullptr"; + std::string resName = Res->GetGlobalName(); + if (!resName.empty()) + return resName; + if (pDebugModule) { + DxilModule &DM = pDebugModule->GetOrCreateDxilModule(); + switch (Res->GetClass()) { + case DXIL::ResourceClass::CBuffer: + return DM.GetCBuffer(Res->GetID()).GetGlobalName(); + case DXIL::ResourceClass::Sampler: + return DM.GetSampler(Res->GetID()).GetGlobalName(); + case DXIL::ResourceClass::SRV: + return DM.GetSRV(Res->GetID()).GetGlobalName(); + case DXIL::ResourceClass::UAV: + return DM.GetUAV(Res->GetID()).GetGlobalName(); + default: + return "Invalid Resource"; + } + } + // When names have been stripped, use class and binding location to + // identify the resource. Format is roughly: + // Allocated: (CB|T|U|S): ((cb|t|u|s)[] + // space) Unallocated: (CB|T|U|S): (no bind + // location) Example: U0: TypedBuffer (u5[2] space1) + // [] and space skipped if 1 and 0 respectively. + return (Twine(Res->GetResIDPrefix()) + Twine(Res->GetID()) + ": " + + Twine(Res->GetResKindName()) + + (Res->IsAllocated() ? (" (" + Twine(Res->GetResBindPrefix()) + + Twine(Res->GetLowerBound()) + + (Res->IsUnbounded() ? Twine("[unbounded]") + : (Res->GetRangeSize() != 1) + ? "[" + Twine(Res->GetRangeSize()) + "]" + : Twine()) + + ((Res->GetSpaceID() != 0) + ? " space" + Twine(Res->GetSpaceID()) + : Twine()) + + ")") + : Twine(" (no bind location)"))) + .str(); +} + +void ValidationContext::EmitResourceError(const hlsl::DxilResourceBase *Res, + ValidationRule rule) { + std::string QuotedRes = " '" + GetResourceName(Res) + "'"; + dxilutil::EmitErrorOnContext(M.getContext(), + GetValidationRuleText(rule) + QuotedRes); + Failed = true; +} + +void ValidationContext::EmitResourceFormatError( + const hlsl::DxilResourceBase *Res, ValidationRule rule, + ArrayRef args) { + std::string QuotedRes = " '" + GetResourceName(Res) + "'"; + std::string ruleText = GetValidationRuleText(rule); + FormatRuleText(ruleText, args); + dxilutil::EmitErrorOnContext(M.getContext(), ruleText + QuotedRes); + Failed = true; +} + +bool ValidationContext::IsDebugFunctionCall(Instruction *I) { + return isa(I); +} + +Instruction *ValidationContext::GetDebugInstr(Instruction *I) { + DXASSERT_NOMSG(I); + if (pDebugModule) { + // Look up the matching instruction in the debug module. + llvm::Function *Fn = I->getParent()->getParent(); + llvm::Function *DbgFn = pDebugModule->getFunction(Fn->getName()); + if (DbgFn) { + // Linear lookup, but then again, failing validation is rare. + inst_iterator it = inst_begin(Fn); + inst_iterator dbg_it = inst_begin(DbgFn); + while (IsDebugFunctionCall(&*dbg_it)) + ++dbg_it; + while (&*it != I) { + ++it; + ++dbg_it; + while (IsDebugFunctionCall(&*dbg_it)) + ++dbg_it; + } + return &*dbg_it; + } + } + return I; +} + +// Emit Error or note on instruction `I` with `Msg`. +// If `isError` is true, `Rule` may omit repeated errors +void ValidationContext::EmitInstrDiagMsg(Instruction *I, ValidationRule Rule, + std::string Msg, bool isError) { + BasicBlock *BB = I->getParent(); + Function *F = BB->getParent(); + + Instruction *DbgI = GetDebugInstr(I); + if (isError) { + if (const DebugLoc L = DbgI->getDebugLoc()) { + // Instructions that get scalarized will likely hit + // this case. Avoid redundant diagnostic messages. + if (Rule == LastRuleEmit && L == LastDebugLocEmit) { + return; + } + LastRuleEmit = Rule; + LastDebugLocEmit = L; + } + dxilutil::EmitErrorOnInstruction(DbgI, Msg); + } else { + dxilutil::EmitNoteOnContext(DbgI->getContext(), Msg); + } + + // Add llvm information as a note to instruction string + std::string InstrStr; + raw_string_ostream InstrStream(InstrStr); + I->print(InstrStream, slotTracker); + InstrStream.flush(); + StringRef InstrStrRef = InstrStr; + InstrStrRef = InstrStrRef.ltrim(); // Ignore indentation + Msg = "at '" + InstrStrRef.str() + "'"; + + // Print the parent block name + Msg += " in block '"; + if (!BB->getName().empty()) { + Msg += BB->getName(); + } else { + unsigned idx = 0; + for (auto i = F->getBasicBlockList().begin(), + e = F->getBasicBlockList().end(); + i != e; ++i) { + if (BB == &(*i)) { + break; + } + idx++; + } + Msg += "#" + std::to_string(idx); + } + Msg += "'"; + + // Print the function name + Msg += " of function '" + F->getName().str() + "'."; + + dxilutil::EmitNoteOnContext(DbgI->getContext(), Msg); + + Failed = true; +} + +void ValidationContext::EmitInstrError(Instruction *I, ValidationRule rule) { + EmitInstrDiagMsg(I, rule, GetValidationRuleText(rule)); +} + +void ValidationContext::EmitInstrNote(Instruction *I, std::string Msg) { + EmitInstrDiagMsg(I, LastRuleEmit, Msg, false); +} + +void ValidationContext::EmitInstrFormatError(Instruction *I, + ValidationRule rule, + ArrayRef args) { + std::string ruleText = GetValidationRuleText(rule); + FormatRuleText(ruleText, args); + EmitInstrDiagMsg(I, rule, ruleText); +} + +void ValidationContext::EmitSignatureError(DxilSignatureElement *SE, + ValidationRule rule) { + EmitFormatError(rule, {SE->GetName()}); +} + +void ValidationContext::EmitTypeError(Type *Ty, ValidationRule rule) { + std::string O; + raw_string_ostream OSS(O); + Ty->print(OSS); + EmitFormatError(rule, {OSS.str()}); +} + +void ValidationContext::EmitFnError(Function *F, ValidationRule rule) { + if (pDebugModule) + if (Function *dbgF = pDebugModule->getFunction(F->getName())) + F = dbgF; + dxilutil::EmitErrorOnFunction(M.getContext(), F, GetValidationRuleText(rule)); + Failed = true; +} + +void ValidationContext::EmitFnFormatError(Function *F, ValidationRule rule, + ArrayRef args) { + std::string ruleText = GetValidationRuleText(rule); + FormatRuleText(ruleText, args); + if (pDebugModule) + if (Function *dbgF = pDebugModule->getFunction(F->getName())) + F = dbgF; + dxilutil::EmitErrorOnFunction(M.getContext(), F, ruleText); + Failed = true; +} + +void ValidationContext::EmitFnAttributeError(Function *F, StringRef Kind, + StringRef Value) { + EmitFnFormatError(F, ValidationRule::DeclFnAttribute, + {F->getName(), Kind, Value}); +} + +} // namespace hlsl diff --git a/lib/DxilValidation/DxilValidationUtils.h b/lib/DxilValidation/DxilValidationUtils.h new file mode 100644 index 0000000000..693c3756e1 --- /dev/null +++ b/lib/DxilValidation/DxilValidationUtils.h @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// DxilValidationUttils.h // +// Copyright (C) Microsoft Corporation. All rights reserved. // +// This file is distributed under the University of Illinois Open Source // +// License. See LICENSE.TXT for details. // +// // +// This file provides utils for validating DXIL. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "dxc/DXIL/DxilConstants.h" +#include "dxc/DXIL/DxilResourceProperties.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/ModuleSlotTracker.h" + +#include +#include +#include +#include + +using namespace llvm; + +namespace llvm { +class Module; +class Function; +class DataLayout; +class Metadata; +class Value; +class GlobalVariable; +class Instruction; +class Type; +} // namespace llvm + +namespace hlsl { + +/////////////////////////////////////////////////////////////////////////////// +// Validation rules. +#include "DxilValidation.inc" + +const char *GetValidationRuleText(ValidationRule value); + +class DxilEntryProps; +class DxilModule; +class DxilResourceBase; +class DxilSignatureElement; + +// Save status like output write for entries. +struct EntryStatus { + bool hasOutputPosition[DXIL::kNumOutputStreams]; + unsigned OutputPositionMask[DXIL::kNumOutputStreams]; + std::vector outputCols; + std::vector patchConstOrPrimCols; + bool m_bCoverageIn, m_bInnerCoverageIn; + bool hasViewID; + unsigned domainLocSize; + EntryStatus(DxilEntryProps &entryProps); +}; + +struct ValidationContext { + bool Failed = false; + Module &M; + Module *pDebugModule; + DxilModule &DxilMod; + const Type *HandleTy; + const DataLayout &DL; + DebugLoc LastDebugLocEmit; + ValidationRule LastRuleEmit; + std::unordered_set entryFuncCallSet; + std::unordered_set patchConstFuncCallSet; + std::unordered_map UavCounterIncMap; + std::unordered_map HandleResIndexMap; + // TODO: save resource map for each createHandle/createHandleForLib. + std::unordered_map ResPropMap; + std::unordered_map> PatchConstantFuncMap; + std::unordered_map> entryStatusMap; + bool isLibProfile; + const unsigned kDxilControlFlowHintMDKind; + const unsigned kDxilPreciseMDKind; + const unsigned kDxilNonUniformMDKind; + const unsigned kLLVMLoopMDKind; + unsigned m_DxilMajor, m_DxilMinor; + ModuleSlotTracker slotTracker; + std::unique_ptr pCallGraph; + + ValidationContext(Module &llvmModule, Module *DebugModule, + DxilModule &dxilModule); + + void PropagateResMap(Value *V, DxilResourceBase *Res); + void BuildResMap(); + bool HasEntryStatus(Function *F); + EntryStatus &GetEntryStatus(Function *F); + CallGraph &GetCallGraph(); + DxilResourceProperties GetResourceFromVal(Value *resVal); + + void EmitGlobalVariableFormatError(GlobalVariable *GV, ValidationRule rule, + ArrayRef args); + // This is the least desirable mechanism, as it has no context. + void EmitError(ValidationRule rule); + + void FormatRuleText(std::string &ruleText, ArrayRef args); + void EmitFormatError(ValidationRule rule, ArrayRef args); + + void EmitMetaError(Metadata *Meta, ValidationRule rule); + + // Use this instead of DxilResourceBase::GetGlobalName + std::string GetResourceName(const hlsl::DxilResourceBase *Res); + + void EmitResourceError(const hlsl::DxilResourceBase *Res, + ValidationRule rule); + + void EmitResourceFormatError(const hlsl::DxilResourceBase *Res, + ValidationRule rule, ArrayRef args); + + bool IsDebugFunctionCall(Instruction *I); + + Instruction *GetDebugInstr(Instruction *I); + + // Emit Error or note on instruction `I` with `Msg`. + // If `isError` is true, `Rule` may omit repeated errors + void EmitInstrDiagMsg(Instruction *I, ValidationRule Rule, std::string Msg, + bool isError = true); + void EmitInstrError(Instruction *I, ValidationRule rule); + + void EmitInstrNote(Instruction *I, std::string Msg); + + void EmitInstrFormatError(Instruction *I, ValidationRule rule, + ArrayRef args); + + void EmitSignatureError(DxilSignatureElement *SE, ValidationRule rule); + + void EmitTypeError(Type *Ty, ValidationRule rule); + + void EmitFnError(Function *F, ValidationRule rule); + + void EmitFnFormatError(Function *F, ValidationRule rule, + ArrayRef args); + + void EmitFnAttributeError(Function *F, StringRef Kind, StringRef Value); +}; + +uint32_t ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule); +} // namespace hlsl diff --git a/lib/HLSL/CMakeLists.txt b/lib/HLSL/CMakeLists.txt index 0f1e3fd381..947fc4c14f 100644 --- a/lib/HLSL/CMakeLists.txt +++ b/lib/HLSL/CMakeLists.txt @@ -2,7 +2,6 @@ # This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. add_hlsl_hctgen(DxcOptimizer OUTPUT DxcOptimizer.inc BUILD_DIR) -add_hlsl_hctgen(DxilValidation OUTPUT DxilValidationImpl.inc BUILD_DIR) add_llvm_library(LLVMHLSL ComputeViewIdState.cpp @@ -32,7 +31,6 @@ add_llvm_library(LLVMHLSL DxilTargetTransformInfo.cpp DxilTranslateRawBuffer.cpp DxilExportMap.cpp - DxilValidation.cpp DxcOptimizer.cpp HLDeadFunctionElimination.cpp HLExpandStoreIntrinsics.cpp diff --git a/lib/HLSL/WaveSensitivityAnalysis.cpp b/lib/HLSL/WaveSensitivityAnalysis.cpp index 2828e6e3fc..c87230dbf5 100644 --- a/lib/HLSL/WaveSensitivityAnalysis.cpp +++ b/lib/HLSL/WaveSensitivityAnalysis.cpp @@ -11,33 +11,13 @@ /////////////////////////////////////////////////////////////////////////////// #include "dxc/DXIL/DxilInstructions.h" -#include "dxc/DXIL/DxilModule.h" #include "dxc/DXIL/DxilOperations.h" -#include "dxc/DXIL/DxilShaderModel.h" -#include "dxc/DxilContainer/DxilContainer.h" #include "dxc/HLSL/DxilGenerationPass.h" -#include "dxc/HLSL/DxilValidation.h" -#include "dxc/HLSL/HLModule.h" -#include "dxc/HLSL/HLOperations.h" #include "dxc/Support/Global.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitVector.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/IR/Constants.h" -#include "llvm/IR/DiagnosticInfo.h" -#include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" - -#ifdef _WIN32 -#include -#endif -#include "llvm/Support/raw_ostream.h" -#include using namespace llvm; using std::map; diff --git a/projects/dxilconv/tools/dxilconv/CMakeLists.txt b/projects/dxilconv/tools/dxilconv/CMakeLists.txt index d5f3341e75..3c1ce507ed 100644 --- a/projects/dxilconv/tools/dxilconv/CMakeLists.txt +++ b/projects/dxilconv/tools/dxilconv/CMakeLists.txt @@ -13,7 +13,7 @@ target_link_libraries(dxilconv PRIVATE LLVMDXIL LLVMDxilContainer LLVMDxilRootSignature - LLVMHLSL + LLVMDxilValidation LLVMMSSupport LLVMScalarOpts ) @@ -23,4 +23,4 @@ set_target_properties(dxilconv OUTPUT_NAME "dxilconv" ) -add_dependencies(dxilconv DxcEtw) \ No newline at end of file +add_dependencies(dxilconv DxcEtw) diff --git a/tools/clang/tools/dxcompiler/dxclinker.cpp b/tools/clang/tools/dxcompiler/dxclinker.cpp index 2305b4d43c..2446593238 100644 --- a/tools/clang/tools/dxcompiler/dxclinker.cpp +++ b/tools/clang/tools/dxcompiler/dxclinker.cpp @@ -23,8 +23,8 @@ #include "llvm/ADT/SmallVector.h" #include +#include "dxc/DxilValidation/DxilValidation.h" #include "dxc/HLSL/DxilLinker.h" -#include "dxc/HLSL/DxilValidation.h" #include "dxc/Support/HLSLOptions.h" #include "dxc/Support/Unicode.h" #include "dxc/Support/microcom.h" diff --git a/tools/clang/tools/dxcvalidator/CMakeLists.txt b/tools/clang/tools/dxcvalidator/CMakeLists.txt index ae58e13883..3ad0a6bf75 100644 --- a/tools/clang/tools/dxcvalidator/CMakeLists.txt +++ b/tools/clang/tools/dxcvalidator/CMakeLists.txt @@ -7,7 +7,7 @@ set( LLVM_LINK_COMPONENTS dxcsupport DXIL DxilContainer - HLSL + DxilValidation Option # option library Support # just for assert and raw streams ) diff --git a/tools/clang/tools/dxcvalidator/dxcvalidator.cpp b/tools/clang/tools/dxcvalidator/dxcvalidator.cpp index b7e363f8e3..a9d721bbec 100644 --- a/tools/clang/tools/dxcvalidator/dxcvalidator.cpp +++ b/tools/clang/tools/dxcvalidator/dxcvalidator.cpp @@ -14,7 +14,7 @@ #include "llvm/IR/LLVMContext.h" #include "dxc/DxilContainer/DxilContainer.h" -#include "dxc/HLSL/DxilValidation.h" +#include "dxc/DxilValidation/DxilValidation.h" #include "dxc/Support/WinIncludes.h" #include "dxc/dxcapi.h" #include "dxcvalidator.h" diff --git a/tools/clang/tools/dxrfallbackcompiler/dxcdxrfallbackcompiler.cpp b/tools/clang/tools/dxrfallbackcompiler/dxcdxrfallbackcompiler.cpp index 5be551ec1f..84bb7b7281 100644 --- a/tools/clang/tools/dxrfallbackcompiler/dxcdxrfallbackcompiler.cpp +++ b/tools/clang/tools/dxrfallbackcompiler/dxcdxrfallbackcompiler.cpp @@ -21,7 +21,7 @@ #include "dxc/HLSL/DxilLinker.h" #include "dxc/DXIL/DxilModule.h" #include "dxc/DXIL/DxilUtil.h" -#include "dxc/HLSL/DxilValidation.h" +#include "dxc/DxilValidation/DxilValidation.h" #include "dxc/Support/FileIOHelper.h" #include "dxc/Support/dxcapi.impl.h" #include "dxcutil.h" diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 888d70f305..919610e420 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -1941,7 +1941,6 @@ def RunCodeTagUpdate(file_path): "lib/DXIL/DxilShaderModel.cpp", "include/dxc/DXIL/DxilConstants.h", "include/dxc/DXIL/DxilShaderModel.h", - "include/dxc/HLSL/DxilValidation.h", "include/dxc/Support/HLSLOptions.td", "include/dxc/DXIL/DxilInstructions.h", "lib/HLSL/DxcOptimizer.cpp",