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

Open source dxil.dll #6770

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open

Conversation

python3kgae
Copy link
Contributor

This pull request introduces the open-source implementation of dxil.dll, including hashing and validation functionalities for DXIL containers.

DxilHash.cpp: Implements DXBC/DXIL container hashing functions.
dxcvalidatorp.cpp: Implements the DirectX Validator object, including signing support.
dxildll.cpp: Implements the DxcCreateInstance and DllMain functions for the dxil.dll module.
DxcSigningContainerBuilder.cpp: Implements the Dxil Container Builder.

This is first part for #6769

@python3kgae python3kgae requested a review from a team as a code owner July 11, 2024 17:07
Copy link
Contributor

github-actions bot commented Jul 11, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@llvm-beanz
Copy link
Collaborator

@python3kgae, I realize that you didn't write all this code and you're really just copying it from an internal fork into the public repo. That said, there are a lot of small things about this code that don't meet the LLVM coding standards. It might be worth spending a little bit of time cleaning up some of the low hanging fruit so that the code goes into the public repo in a cleaner state.

I pointed out a few places where the terminology "signing" is used to describe the hashing process. I think we need to correct all of those (there are still more). I know that historically this was called "signing", but it is not a signature and calling it a signature places an unmet expectation on what the code does.

@python3kgae
Copy link
Contributor Author

@python3kgae, I realize that you didn't write all this code and you're really just copying it from an internal fork into the public repo. That said, there are a lot of small things about this code that don't meet the LLVM coding standards. It might be worth spending a little bit of time cleaning up some of the low hanging fruit so that the code goes into the public repo in a cleaner state.

I pointed out a few places where the terminology "signing" is used to describe the hashing process. I think we need to correct all of those (there are still more). I know that historically this was called "signing", but it is not a signature and calling it a signature places an unmet expectation on what the code does.

All the 'signning's are replaced with 'hashing' already.
I can update pSigned to pHashed next.
Do you have anything else in mind?

@llvm-beanz
Copy link
Collaborator

All the 'signning's are replaced with 'hashing' already. I can update pSigned to pHashed next. Do you have anything else in mind?

We spent months last year reviewing and cleaning up the SM 6.8 code to get closer to meeting LLVM's coding standards. I think spending a little time on this PR makes sense.

Some things to look for:

I'm not asking that you rewrite this code, just spend a few minutes making sure that the code we introduce to DXC is a little nicer than what we had internally.

@python3kgae
Copy link
Contributor Author

All the 'signning's are replaced with 'hashing' already. I can update pSigned to pHashed next. Do you have anything else in mind?

We spent months last year reviewing and cleaning up the SM 6.8 code to get closer to meeting LLVM's coding standards. I think spending a little time on this PR makes sense.

Some things to look for:

I'm not asking that you rewrite this code, just spend a few minutes making sure that the code we introduce to DXC is a little nicer than what we had internally.

Updated Signed to Hashed.

For other issues, https://github.com/microsoft/DirectXShaderCompiler/blob/main/tools/clang/tools/dxcompiler/dxcvalidator.cpp need to be updated as well.

Will do the job when work on Build the DXIL validator library both as a dynamic and static library where these 2 validators could be merged into one.

@damyanp
Copy link
Member

damyanp commented Jul 18, 2024

Will do the job when work on Build the DXIL validator library both as a dynamic and static library where these 2 validators could be merged into one.

Can you help me understand the benefits of doing this later rather than now in this initial PR? I assume that this change comes in with enough testing that we can make NFC refactors to the code without worrying about breaking it, and given that a bar for code style/quality has been set with SM6.8 work it isn't obvious to me why we'd change that for this change.

@python3kgae
Copy link
Contributor Author

Will do the job when work on Build the DXIL validator library both as a dynamic and static library where these 2 validators could be merged into one.

Can you help me understand the benefits of doing this later rather than now in this initial PR? I assume that this change comes in with enough testing that we can make NFC refactors to the code without worrying about breaking it, and given that a bar for code style/quality has been set with SM6.8 work it isn't obvious to me why we'd change that for this change.

The code in
https://github.com/microsoft/DirectXShaderCompiler/blob/main/tools/clang/tools/dxcompiler/dxcvalidator.cpp and the validator code in this change are very similar and have similar issues.
Build the DXIL validator library both as a dynamic and static library will need to merge these 2 validators, where we only need to do the style change in one place.
If we do the style change only in the validator for current PR, when we merge the 2 validators, the code will get more difference just caused by style change.

@damyanp
Copy link
Member

damyanp commented Jul 18, 2024

The code in https://github.com/microsoft/DirectXShaderCompiler/blob/main/tools/clang/tools/dxcompiler/dxcvalidator.cpp and the validator code in this change are very similar and have similar issues.

Oh, I didn't realize that this change was essentially adding a whole load of duplicate code. I think we should figure out how to deduplicate this before completing the PR, otherwise we're just adding more technical debt to the project.


static void HashAndUpdateOrCopy(uint32_t Flags, IDxcBlob *Shader,
IDxcBlob **Hashed) {
if (Flags & DxcValidatorFlags_InPlaceEdit) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we were to add a flag to skip hashing and container modification called DxcValidatorFlags_SkipHash, we could implement it like so:

Suggested change
if (Flags & DxcValidatorFlags_InPlaceEdit) {
if (Flags & DxcValidatorFlags_SkipHash) {
*Hashed = Shader;
Shader->AddRef();
} else if (Flags & DxcValidatorFlags_InPlaceEdit) {

Comment on lines 69 to 70
// Possible gotcha: the blob allocated here is tied to this .dll, so the
// DLL shouldn't be unloaded before the blob is released.
Copy link
Contributor

Choose a reason for hiding this comment

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

That's generally how returned blobs work in the DXC APIs. Do we need to point this out as a comment here? It probably belongs in the API documentation, and potentially as a comment on the API definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

@@ -64,7 +64,7 @@ HRESULT STDMETHODCALLTYPE DxcValidator::Validate(
IDxcOperationResult *
*ppResult // Validation output status, buffer, and errors
) {
return hlsl::validate(pShader, Flags, ppResult);
return hlsl::validate(pShader, Flags, true, ppResult);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not be true, and I don't think we should have this implementation of IDxcValidator in dxrfallbackcompiler in the first place.

@@ -155,24 +222,16 @@ runRootSignatureValidation(IDxcBlob *Shader,
uint32_t hlsl::validate(
IDxcBlob *Shader, // Shader to validate.
uint32_t Flags, // Validation flags.
bool IsInternalValidator, // Run internal validator.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should need this flag, ultimately. But that will require a cleanup of how we do internal validation.

Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, it appears that the only way this flag is used is to ignore DxcValidatorFlags_ModuleOnly if it's not set. I don't think this is right. See my comment on the use of this flag in hlsl::validateWithDebug.

static uint32_t runValidation(
IDxcBlob *Shader,
uint32_t Flags, // Validation flags.
llvm::Module *Module, // Module to validate, if available.
llvm::Module *DebugModule, // Debug module to validate, if available
AbstractMemoryStream *DiagMemStream) {
AbstractMemoryStream *DiagMemStream, IDxcBlob **Hashed) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This Hashed output pointer is not written to in this function, nor does it compute the hash.

This function is for the internal case, which I think needs an overhaul, especially now that we are integrating the hash here.

It's likely best if we did the overhaul as a separate change before integrating the hash changes. It should greatly simplify the scenarios.

We should get rid of the special internal validator case where it accepts the main module as a llvm::Module and always validate the serialized bitcode in the container. We will want to keep the debug module argument, so it may be passed along internally, avoiding extra deserialization, since it's only used for diagnostic locations, and not the thing being validated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, so this is really meant to be the inner function used by both code paths that have debug bitcode and the internal path that has a debug module. We should probably use this in the other runValidation overload, and in the case of DxcValidatorFlags_ModuleOnly use a different function entirely to call this one which deserialized the debug module and does not have a Hashed output at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

if (Flags & DxcValidatorFlags_RootSignatureOnly)
ValidationStatus =
runRootSignatureValidation(Shader, DiagMemStream, Flags, &HashedBlob);
else if ((Flags & DxcValidatorFlags_ModuleOnly) && IsInternalValidator)
Copy link
Contributor

Choose a reason for hiding this comment

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

DxcValidatorFlags_ModuleOnly should allow validation of raw bitcode, and disable hashing. IsInternalValidator should not be required, and I don't think this code handles this case properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Current dxil.dll doesn't support DxcValidatorFlags_ModuleOnly, just return DXC_E_CONTAINER_INVALID.
Shall we change that in this PR?

&DiagContext, true);
std::unique_ptr<llvm::Module> DebugModule;
if (OptDebugBitcode) {
hr = ValidateLoadModule((const char *)OptDebugBitcode->Ptr,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why has this been removed? We should be passing the loaded debug module through to runValidation, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Nevermind, it's just missing from the DxcValidatorFlags_ModuleOnly case, I'll make a separate comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I think this original structure should be restored so that a debug module may be passed down to a validateWithOptModules function that will apply the hash when applicable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Recovered debug module.

ValidationStatus =
runRootSignatureValidation(Shader, DiagMemStream, Flags, &HashedBlob);
else if ((Flags & DxcValidatorFlags_ModuleOnly) && IsInternalValidator)
ValidationStatus = runValidation(Shader, Flags, nullptr, nullptr,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be passing the OptDebugBitcode along, though that will require deserialization in this overloaded runValidation path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

static uint32_t
runRootSignatureValidation(IDxcBlob *Shader,
AbstractMemoryStream *DiagMemStream) {
static uint32_t runValidation(IDxcBlob *Shader,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be better if this was combined with the other overload, and the combined function handled whether DxcValidatorFlags_ModuleOnly was being performed, or if we have a full container to sign. Right now it's confusing, and an internal path with a debug module can't go through the signing path.

Copy link
Contributor

Choose a reason for hiding this comment

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

This can be accomplished by moving debug module serialization out, but it will also require a change to ValidateDxilContainer to take the llvm::Module* instead of the serialized bitcode.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... perhaps this isn't sufficient, since it might need to load the debug module from the container instead. So maybe we just need to add a debug llvm::Module argument to pass along if we already have one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Development

Successfully merging this pull request may close these issues.

4 participants