Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a test dxil lib for testing loadability #5952

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions include/dxc/HLSL/DxilValidator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "dxc/Support/Global.h"
#include "dxc/Support/WinIncludes.h"
#include "dxc/Support/microcom.h"
#include "dxc/dxcapi.h"

namespace llvm {
class Module;
}

namespace hlsl {
class AbstractMemoryStream;
}

class DxilValidator : public IDxcValidator2 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rename DxilValidator to DxcValidatorBase?

It's already a little confusing to have DxilValidation and DxcValidator, but it's a pattern that is used elsewhere, with core implementation in Dxil* and Dxc* being the implementation of the IDxc* interface.

Adding DxilValidator confuses things more, while I think DxcValidatorBase makes the purpose of being a base class for the IDxcValidator implementation clearer.

private:
CComPtr<IMalloc> m_pMalloc;
HRESULT RunValidation(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
llvm::Module *pModule, // Module to validate, if available.
llvm::Module *pDebugModule, // Debug module to validate, if available
hlsl::AbstractMemoryStream *pDiagStream);

HRESULT RunRootSignatureValidation(IDxcBlob *pShader, // Shader to validate.
hlsl::AbstractMemoryStream *pDiagStream);

public:
DxilValidator(IMalloc *pMalloc) : m_pMalloc(pMalloc) {}

// For internal use only.
HRESULT ValidateWithOptModules(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
llvm::Module *pModule, // Module to validate, if available.
llvm::Module *pDebugModule, // Debug module to validate, if available
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
);

// IDxcValidator
HRESULT STDMETHODCALLTYPE Validate(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) override;

// IDxcValidator2
HRESULT STDMETHODCALLTYPE ValidateWithDebug(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
DxcBuffer *pOptDebugBitcode, // Optional debug module bitcode to provide
// line numbers
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) override;
};
1 change: 1 addition & 0 deletions lib/HLSL/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_llvm_library(LLVMHLSL
DxilTranslateRawBuffer.cpp
DxilExportMap.cpp
DxilValidation.cpp
DxilValidator.cpp
DxcOptimizer.cpp
HLDeadFunctionElimination.cpp
HLExpandStoreIntrinsics.cpp
Expand Down
278 changes: 278 additions & 0 deletions lib/HLSL/DxilValidator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
///////////////////////////////////////////////////////////////////////////////
// //
// DxilValidator.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. //
// //
// Implements the DirectX Validator object. //
// //
///////////////////////////////////////////////////////////////////////////////

#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"

#include "dxc/DxilContainer/DxilContainer.h"
#include "dxc/HLSL/DxilValidation.h"
#include "dxc/HLSL/DxilValidator.h"
#include "dxc/Support/WinIncludes.h"

#include "dxc/DxilRootSignature/DxilRootSignature.h"
#include "dxc/Support/FileIOHelper.h"
#include "dxc/Support/Global.h"
#include "dxc/dxcapi.h"
#include "dxc/Support/dxcapi.impl.h"
#include "llvm/Support/MemoryBuffer.h"

#ifdef _WIN32
#include "dxcetw.h"
#endif

using namespace llvm;
using namespace hlsl;

// Utility class for setting and restoring the diagnostic context so we may
// capture errors/warnings
struct DiagRestore {
LLVMContext &Ctx;
void *OrigDiagContext;
LLVMContext::DiagnosticHandlerTy OrigHandler;

DiagRestore(llvm::LLVMContext &Ctx, void *DiagContext) : Ctx(Ctx) {
OrigHandler = Ctx.getDiagnosticHandler();
OrigDiagContext = Ctx.getDiagnosticContext();
Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler,
DiagContext);
}
~DiagRestore() { Ctx.setDiagnosticHandler(OrigHandler, OrigDiagContext); }
};

// Compile a single entry point to the target shader model
HRESULT STDMETHODCALLTYPE DxilValidator::Validate(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) {
DxcThreadMalloc TM(m_pMalloc);
if (ppResult == nullptr)
return E_INVALIDARG;
*ppResult = nullptr;
if (pShader == nullptr || Flags & ~DxcValidatorFlags_ValidMask)
return E_INVALIDARG;
if ((Flags & DxcValidatorFlags_ModuleOnly) &&
(Flags &
(DxcValidatorFlags_InPlaceEdit | DxcValidatorFlags_RootSignatureOnly)))
return E_INVALIDARG;
return ValidateWithOptModules(pShader, Flags, nullptr, nullptr, ppResult);
}

HRESULT STDMETHODCALLTYPE DxilValidator::ValidateWithDebug(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
DxcBuffer *pOptDebugBitcode, // Optional debug module bitcode to provide
// line numbers
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) {
if (ppResult == nullptr)
return E_INVALIDARG;
*ppResult = nullptr;
if (pShader == nullptr || Flags & ~DxcValidatorFlags_ValidMask)
return E_INVALIDARG;
if ((Flags & DxcValidatorFlags_ModuleOnly) &&
(Flags &
(DxcValidatorFlags_InPlaceEdit | DxcValidatorFlags_RootSignatureOnly)))
return E_INVALIDARG;
if (pOptDebugBitcode &&
(pOptDebugBitcode->Ptr == nullptr || pOptDebugBitcode->Size == 0 ||
pOptDebugBitcode->Size >= UINT32_MAX))
return E_INVALIDARG;

HRESULT hr = S_OK;
DxcThreadMalloc TM(m_pMalloc);
try {
LLVMContext Ctx;
CComPtr<AbstractMemoryStream> pDiagStream;
IFT(CreateMemoryStream(m_pMalloc, &pDiagStream));
raw_stream_ostream DiagStream(pDiagStream);
llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
PrintDiagnosticContext DiagContext(DiagPrinter);
Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler,
&DiagContext, true);
std::unique_ptr<llvm::Module> pDebugModule;
if (pOptDebugBitcode) {
IFT(ValidateLoadModule((const char *)pOptDebugBitcode->Ptr,
(uint32_t)pOptDebugBitcode->Size, pDebugModule,
Ctx, DiagStream, /*bLazyLoad*/ false));
}
return ValidateWithOptModules(pShader, Flags, nullptr, pDebugModule.get(),
ppResult);
}
CATCH_CPP_ASSIGN_HRESULT();
return hr;
}

HRESULT DxilValidator::ValidateWithOptModules(
IDxcBlob *pShader, // Shader to validate.
UINT32 Flags, // Validation flags.
llvm::Module *pModule, // Module to validate, if available.
llvm::Module *pDebugModule, // Debug module to validate, if available
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) {
*ppResult = nullptr;
HRESULT hr = S_OK;
HRESULT validationStatus = S_OK;
DxcEtw_DxcValidation_Start();
DxcThreadMalloc TM(m_pMalloc);
try {
CComPtr<AbstractMemoryStream> pDiagStream;
IFT(CreateMemoryStream(m_pMalloc, &pDiagStream));

// Run validation may throw, but that indicates an inability to validate,
// not that the validation failed (eg out of memory).
if (Flags & DxcValidatorFlags_RootSignatureOnly) {
validationStatus = RunRootSignatureValidation(pShader, pDiagStream);
} else {
validationStatus =
RunValidation(pShader, Flags, pModule, pDebugModule, pDiagStream);
}
if (FAILED(validationStatus)) {
std::string msg("Validation failed.\n");
ULONG cbWritten;
pDiagStream->Write(msg.c_str(), msg.size(), &cbWritten);
}
// Assemble the result object.
CComPtr<IDxcBlob> pDiagBlob;
hr = pDiagStream.QueryInterface(&pDiagBlob);
DXASSERT_NOMSG(SUCCEEDED(hr));
IFT(DxcResult::Create(
validationStatus, DXC_OUT_NONE,
{DxcOutputObject::ErrorOutput(
CP_UTF8, // TODO Support DefaultTextCodePage
(LPCSTR)pDiagBlob->GetBufferPointer(), pDiagBlob->GetBufferSize())},
ppResult));
}
CATCH_CPP_ASSIGN_HRESULT();

DxcEtw_DxcValidation_Stop(SUCCEEDED(hr) ? validationStatus : hr);
return hr;
}

HRESULT DxilValidator::RunValidation(
IDxcBlob *pShader,
UINT32 Flags, // Validation flags.
llvm::Module *pModule, // Module to validate, if available.
llvm::Module *pDebugModule, // Debug module to validate, if available
AbstractMemoryStream *pDiagStream) {

// Run validation may throw, but that indicates an inability to validate,
// not that the validation failed (eg out of memory). That is indicated
// by a failing HRESULT, and possibly error messages in the diagnostics
// stream.

raw_stream_ostream DiagStream(pDiagStream);

if (Flags & DxcValidatorFlags_ModuleOnly) {
IFRBOOL(!IsDxilContainerLike(pShader->GetBufferPointer(),
pShader->GetBufferSize()),
E_INVALIDARG);
} else {
IFRBOOL(IsDxilContainerLike(pShader->GetBufferPointer(),
pShader->GetBufferSize()),
DXC_E_CONTAINER_INVALID);
}

if (!pModule) {
DXASSERT_NOMSG(pDebugModule == nullptr);
if (Flags & DxcValidatorFlags_ModuleOnly) {
return ValidateDxilBitcode((const char *)pShader->GetBufferPointer(),
(uint32_t)pShader->GetBufferSize(),
DiagStream);
} else {
return ValidateDxilContainer(pShader->GetBufferPointer(),
pShader->GetBufferSize(), DiagStream);
}
}

llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
PrintDiagnosticContext DiagContext(DiagPrinter);
DiagRestore DR(pModule->getContext(), &DiagContext);

IFR(hlsl::ValidateDxilModule(pModule, pDebugModule));
if (!(Flags & DxcValidatorFlags_ModuleOnly)) {
IFR(ValidateDxilContainerParts(
pModule, pDebugModule,
IsDxilContainerLike(pShader->GetBufferPointer(),
pShader->GetBufferSize()),
(uint32_t)pShader->GetBufferSize()));
}

if (DiagContext.HasErrors() || DiagContext.HasWarnings()) {
return DXC_E_IR_VERIFICATION_FAILED;
}

return S_OK;
}

HRESULT
DxilValidator::RunRootSignatureValidation(IDxcBlob *pShader,
AbstractMemoryStream *pDiagStream) {

const DxilContainerHeader *pDxilContainer = IsDxilContainerLike(
pShader->GetBufferPointer(), pShader->GetBufferSize());
if (!pDxilContainer) {
return DXC_E_IR_VERIFICATION_FAILED;
}

const DxilProgramHeader *pProgramHeader =
GetDxilProgramHeader(pDxilContainer, DFCC_DXIL);
const DxilPartHeader *pPSVPart =
GetDxilPartByType(pDxilContainer, DFCC_PipelineStateValidation);
const DxilPartHeader *pRSPart =
GetDxilPartByType(pDxilContainer, DFCC_RootSignature);
IFRBOOL(pRSPart, DXC_E_MISSING_PART);
if (pProgramHeader) {
// Container has shader part, make sure we have PSV.
IFRBOOL(pPSVPart, DXC_E_MISSING_PART);
}
try {
RootSignatureHandle RSH;
RSH.LoadSerialized((const uint8_t *)GetDxilPartData(pRSPart),
pRSPart->PartSize);
RSH.Deserialize();
raw_stream_ostream DiagStream(pDiagStream);
if (pProgramHeader) {
IFRBOOL(VerifyRootSignatureWithShaderPSV(
RSH.GetDesc(),
GetVersionShaderType(pProgramHeader->ProgramVersion),
GetDxilPartData(pPSVPart), pPSVPart->PartSize, DiagStream),
DXC_E_INCORRECT_ROOT_SIGNATURE);
} else {
IFRBOOL(VerifyRootSignature(RSH.GetDesc(), DiagStream, false),
DXC_E_INCORRECT_ROOT_SIGNATURE);
}
} catch (...) {
return DXC_E_IR_VERIFICATION_FAILED;
}

return S_OK;
}

///////////////////////////////////////////////////////////////////////////////

HRESULT RunInternalValidator(IDxcValidator *pValidator, llvm::Module *pModule,
llvm::Module *pDebugModule, IDxcBlob *pShader,
UINT32 Flags, IDxcOperationResult **ppResult) {
DXASSERT_NOMSG(pValidator != nullptr);
DXASSERT_NOMSG(pModule != nullptr);
DXASSERT_NOMSG(pShader != nullptr);
DXASSERT_NOMSG(ppResult != nullptr);

DxilValidator *pInternalValidator = (DxilValidator *)pValidator;
return pInternalValidator->ValidateWithOptModules(pShader, Flags, pModule,
pDebugModule, ppResult);
}

1 change: 1 addition & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ if (WIN32)
# This target can currently only be built on Windows.
add_llvm_tool_subdirectory(dxexp)
endif (WIN32)
add_llvm_tool_subdirectory(no-sig-dxillib)
# HLSL Change ends

# add_llvm_tool_subdirectory(llc) # HLSL Change
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %dxilver 1.6 | %dxc -E main -T as_6_5 %s | FileCheck %s -check-prefix=CHK_NODB

// CHK_DB: 23:5: error: For amplification shader with entry 'main', payload size 16400 is greater than maximum size of 16384 bytes.
// CHK_NODB: 23:5: error: For amplification shader with entry 'main', payload size 16400 is greater than maximum size of 16384 bytes.
// CHK_NODB: error: For amplification shader with entry 'main', payload size 16400 is greater than maximum size of 16384 bytes.
Copy link
Contributor

@tex3d tex3d Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change shouldn't have been necessary. IDxcValidator2::ValidateWithDebug allows you to provide the serialized debug module, which is normally provided to the external validator when present. This must work with the normal DXIL.dll because these don't normally fail when testing against that. So perhaps there's something wrong with the new test validator?


#define NUM_THREADS 32

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %dxilver 1.6 | %dxc -E main -T ms_6_5 %s | FileCheck %s -check-prefix=CHK_NODB

// CHK_DB: :29: error: For mesh shader with entry 'main', payload size 16404 is greater than maximum size of 16384 bytes.
// CHK_NODB: :29: error: For mesh shader with entry 'main', payload size 16404 is greater than maximum size of 16384 bytes.
// CHK_NODB: error: For mesh shader with entry 'main', payload size 16404 is greater than maximum size of 16384 bytes.

#define MAX_VERT 32
#define MAX_PRIM 16
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// RUN: %dxilver 1.6 | %dxc -T ps_6_5 -enable-16bit-types %s | FileCheck %s

// CHECK-DAG: 13:{{[0-9]+}}: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 14:{{[0-9]+}}: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 15:{{[0-9]+}}: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 16:{{[0-9]+}}: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 17:{{[0-9]+}}: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 18:{{[0-9]+}}: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 19:{{[0-9]+}}: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: 20:{{[0-9]+}}: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Pack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Unpack4x8 not valid in shader model ps_6_5
// CHECK-DAG: error: Opcode Unpack4x8 not valid in shader model ps_6_5

int16_t4 main(int4 input1 : Inputs1, int16_t4 input2 : Inputs2) : SV_Target {
int8_t4_packed ps1 = pack_s8(input1);
Expand Down
Loading