Skip to content

[DirectX] Add Range Overlap validation #152229

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
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 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
3 changes: 3 additions & 0 deletions llvm/include/llvm/Support/DXILABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef LLVM_SUPPORT_DXILABI_H
#define LLVM_SUPPORT_DXILABI_H

#include "llvm/ADT/StringRef.h"
#include <cstdint>

namespace llvm {
Expand Down Expand Up @@ -99,6 +100,8 @@ enum class SamplerFeedbackType : uint32_t {
const unsigned MinWaveSize = 4;
const unsigned MaxWaveSize = 128;

StringRef getResourceClassName(ResourceClass RC);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to use the recently landed function defined here in tangent with enumToStringRef: #152213

Copy link
Contributor

Choose a reason for hiding this comment

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

Bump

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check llvm/lib/Support/DXILABI.cpp, getResourceClassName calls enumToStringRef

Copy link
Contributor

@inbelic inbelic Aug 12, 2025

Choose a reason for hiding this comment

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

Ah okay, I think we could probably just do that inline? Then we don't need to define DXILABI.cpp at all, since this file is defined to just contain "definitions of constants and enums" to be stable in the DXIL

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please check #152229 (comment), bogner explains why we created DXILABI.cpp.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, in that case I think we need to move the getResourceClasses from here to DXILABI. Otherwise, we have a circular dependency between DXILABI and BinaryFormat/DXContainer.h.

Either that or inline this function where it is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The circular dependency shouldn't be an issue, since we use the #ifndef in all files.

I will fix this, but in a follow-up PR, since this change is already out of scope for this patch.

Copy link
Contributor Author

@joaosaffran joaosaffran Aug 13, 2025

Choose a reason for hiding this comment

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

Here is the PR fixing it: #153490


} // namespace dxil
} // namespace llvm

Expand Down
15 changes: 1 addition & 14 deletions llvm/lib/Analysis/DXILResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <optional>
Expand All @@ -29,20 +30,6 @@
using namespace llvm;
using namespace dxil;

static StringRef getResourceClassName(ResourceClass RC) {
switch (RC) {
case ResourceClass::SRV:
return "SRV";
case ResourceClass::UAV:
return "UAV";
case ResourceClass::CBuffer:
return "CBuffer";
case ResourceClass::Sampler:
return "Sampler";
}
llvm_unreachable("Unhandled ResourceClass");
}

static StringRef getResourceKindName(ResourceKind RK) {
switch (RK) {
case ResourceKind::Texture1D:
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ add_llvm_component_library(LLVMSupport
DivisionByConstantInfo.cpp
DAGDeltaAlgorithm.cpp
DJB.cpp
DXILABI.cpp
DynamicAPInt.cpp
ELFAttributes.cpp
ELFAttrParserCompact.cpp
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Support/DXILABI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

Copy link
Contributor

Choose a reason for hiding this comment

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

This needs a file header.

#include "llvm/Support/DXILABI.h"
#include "llvm/BinaryFormat/DXContainer.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Support can't depend on BinaryFormat

#include "llvm/Support/ErrorHandling.h"
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 this is used

#include "llvm/Support/ScopedPrinter.h"

using namespace llvm;
namespace llvm {
namespace dxil {
StringRef getResourceClassName(dxil::ResourceClass RC) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please avoid namespace blocks like this in the cpp file and just qualify the names:

StringRef dxil::getResourceClassName(dxil::ResourceClass RC) {

Some rationale here: https://llvm.org/docs/CodingStandards.html#use-namespace-qualifiers-to-implement-previously-declared-functions

return enumToStringRef(RC, dxbc::getResourceClasses());
}
} // namespace dxil
} // namespace llvm
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing newline at the end of the file.

3 changes: 1 addition & 2 deletions llvm/lib/Target/DirectX/DXContainerGlobals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ void DXContainerGlobals::addRootSignature(Module &M,

auto &RSA = getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
const Function *EntryFunction = MMI.EntryPropertyVec[0].Entry;
const std::optional<mcdxbc::RootSignatureDesc> &RS =
RSA.getDescForFunction(EntryFunction);
const mcdxbc::RootSignatureDesc *RS = RSA.getDescForFunction(EntryFunction);

if (!RS)
return;
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/DirectX/DXILOpLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "DXILOpLowering.h"
#include "DXILConstants.h"
#include "DXILOpBuilder.h"
#include "DXILRootSignature.h"
#include "DXILShaderFlags.h"
#include "DirectX.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -918,6 +919,7 @@ PreservedAnalyses DXILOpLowering::run(Module &M, ModuleAnalysisManager &MAM) {
PA.preserve<DXILResourceAnalysis>();
PA.preserve<DXILMetadataAnalysis>();
PA.preserve<ShaderFlagsAnalysis>();
PA.preserve<RootSignatureAnalysis>();
return PA;
}

Expand Down Expand Up @@ -945,6 +947,7 @@ class DXILOpLoweringLegacy : public ModulePass {
AU.addPreserved<DXILResourceWrapperPass>();
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
AU.addPreserved<ShaderFlagsAnalysisWrapper>();
AU.addPreserved<RootSignatureAnalysisWrapper>();
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll need to preserve the root signature analysis in the new-PM version of the pass as well (that is, in DXILOpLowering::run)

}
};
char DXILOpLoweringLegacy::ID = 0;
Expand Down
174 changes: 169 additions & 5 deletions llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "DXILPostOptimizationValidation.h"
#include "DXILRootSignature.h"
#include "DXILShaderFlags.h"
#include "DirectX.h"
#include "llvm/ADT/SmallString.h"
Expand All @@ -17,13 +18,43 @@
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/DXILABI.h"

#define DEBUG_TYPE "dxil-post-optimization-validation"

using namespace llvm;
using namespace llvm::dxil;

namespace {
static ResourceClass toResourceClass(dxbc::DescriptorRangeType RangeType) {
using namespace dxbc;
switch (RangeType) {
case DescriptorRangeType::SRV:
return ResourceClass::SRV;
case DescriptorRangeType::UAV:
return ResourceClass::UAV;
case DescriptorRangeType::CBV:
return ResourceClass::CBuffer;
case DescriptorRangeType::Sampler:
return ResourceClass::Sampler;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll need an llvm_unreachable() outside of the switch block or MSVC will get upset that not all control paths return a value.


static ResourceClass toResourceClass(dxbc::RootParameterType Type) {
using namespace dxbc;
switch (Type) {
case RootParameterType::Constants32Bit:
return ResourceClass::CBuffer;
case RootParameterType::SRV:
return ResourceClass::SRV;
case RootParameterType::UAV:
return ResourceClass::UAV;
case RootParameterType::CBV:
return ResourceClass::CBuffer;
case dxbc::RootParameterType::DescriptorTable:
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

break isn't necessary after unreachable

}
llvm_unreachable("Unconvertible RootParameterType");
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be better to have a different llvm_unreachable() with a message like "DescriptorTable is not convertible to ResourceClass" in the DescriptorTable case, and then this one is truly "Unknown RootParameterType"? That case could be hit because of programmer-error (we call this function without satisfying its invariants) whereas a value outside of the enum range should be truly unreachable.

}

static void reportInvalidDirection(Module &M, DXILResourceMap &DRM) {
for (const auto &UAV : DRM.uavs()) {
Expand Down Expand Up @@ -84,8 +115,126 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
}
}

static void
reportOverlappingRegisters(Module &M,
const llvm::hlsl::BindingInfoBuilder::Binding &R1,
const llvm::hlsl::BindingInfoBuilder::Binding &R2) {
SmallString<128> Message;

raw_svector_ostream OS(Message);
OS << "resource " << getResourceClassName(R1.RC) << " (space=" << R1.Space
<< ", registers=[" << R1.LowerBound << ", " << R1.UpperBound
<< "]) overlaps with resource " << getResourceClassName(R2.RC)
<< " (space=" << R2.Space << ", registers=[" << R2.LowerBound << ", "
<< R2.UpperBound << "])";
M.getContext().diagnose(DiagnosticInfoGeneric(Message));
}

static dxbc::ShaderVisibility
tripleToVisibility(llvm::Triple::EnvironmentType ET) {
switch (ET) {
case Triple::Pixel:
return dxbc::ShaderVisibility::Pixel;
case Triple::Vertex:
return dxbc::ShaderVisibility::Vertex;
case Triple::Geometry:
return dxbc::ShaderVisibility::Geometry;
case Triple::Hull:
return dxbc::ShaderVisibility::Hull;
case Triple::Domain:
return dxbc::ShaderVisibility::Domain;
case Triple::Mesh:
return dxbc::ShaderVisibility::Mesh;
case Triple::Compute:
return dxbc::ShaderVisibility::All;
default:
llvm_unreachable("Invalid triple to shader stage conversion");
}
}

static void validateRootSignature(Module &M,
const mcdxbc::RootSignatureDesc &RSD,
dxil::ModuleMetadataInfo &MMI) {

hlsl::BindingInfoBuilder Builder;
dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
SmallVector<char> IDs;
Copy link
Contributor

Choose a reason for hiding this comment

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

Are duplicates possible? Do we need to unique them at all? If not, then we probably don't need IDs at all - we can just pass nullptr as the cookie if we're not going to do anything with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Duplicates are possible, if we have a root signature like: SRV(t0), DescriptorTable(SRV(t0)).

Copy link
Contributor

Choose a reason for hiding this comment

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

Is using the IDs vector more standard than just &ParamInfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if there is a standard, since this isn't used in a lot of places. But I tried to follow a similar pattern as this test:

Copy link
Contributor

Choose a reason for hiding this comment

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

Using ParamInfo would allow you to access &ParamInfo in the error reporting function for more detail, which is useful in general though it looks like you didn't need it.

However, there are two reasons just using &ParamInfo would still be better:

  • It's already a unique ID with sufficient lifetime, so adding a separate object just to use its address seems inefficient
  • Using a SmallVector<char> here that we fill as we go is unsafe/incorrect - if the vector is resized (because it gets too big for the current storage) the addresses of the objects in it will be invalidated, so we'll be comparing invalid pointers and hitting UB here.

for (const mcdxbc::RootParameterInfo &ParamInfo : RSD.ParametersContainer) {
dxbc::ShaderVisibility ParamVisibility =
static_cast<dxbc::ShaderVisibility>(ParamInfo.Header.ShaderVisibility);
if (ParamVisibility != dxbc::ShaderVisibility::All &&
ParamVisibility != Visibility)
continue;
dxbc::RootParameterType ParamType =
static_cast<dxbc::RootParameterType>(ParamInfo.Header.ParameterType);
switch (ParamType) {
case dxbc::RootParameterType::Constants32Bit: {
dxbc::RTS0::v1::RootConstants Const =
RSD.ParametersContainer.getConstant(ParamInfo.Location);
Builder.trackBinding(dxil::ResourceClass::CBuffer, Const.RegisterSpace,
Const.ShaderRegister, Const.ShaderRegister,
&IDs.emplace_back());
break;
}

case dxbc::RootParameterType::SRV:
case dxbc::RootParameterType::UAV:
case dxbc::RootParameterType::CBV: {
dxbc::RTS0::v2::RootDescriptor Desc =
RSD.ParametersContainer.getRootDescriptor(ParamInfo.Location);
Builder.trackBinding(toResourceClass(static_cast<dxbc::RootParameterType>(
ParamInfo.Header.ParameterType)),
Desc.RegisterSpace, Desc.ShaderRegister,
Desc.ShaderRegister, &IDs.emplace_back());

break;
}
case dxbc::RootParameterType::DescriptorTable: {
const mcdxbc::DescriptorTable &Table =
RSD.ParametersContainer.getDescriptorTable(ParamInfo.Location);

for (const dxbc::RTS0::v2::DescriptorRange &Range : Table.Ranges) {
uint32_t UpperBound =
Range.NumDescriptors == ~0U
? Range.BaseShaderRegister
: Range.BaseShaderRegister + Range.NumDescriptors - 1;
Builder.trackBinding(
toResourceClass(
static_cast<dxbc::DescriptorRangeType>(Range.RangeType)),
Range.RegisterSpace, Range.BaseShaderRegister, UpperBound,
&IDs.emplace_back());
}
break;
}
}
}

for (const dxbc::RTS0::v1::StaticSampler &S : RSD.StaticSamplers)
Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,
S.ShaderRegister, S.ShaderRegister,
&IDs.emplace_back());

Builder.calculateBindingInfo(
[&M](const llvm::hlsl::BindingInfoBuilder &Builder,
const llvm::hlsl::BindingInfoBuilder::Binding &ReportedBinding) {
const llvm::hlsl::BindingInfoBuilder::Binding &Overlaping =
Builder.findOverlapping(ReportedBinding);
reportOverlappingRegisters(M, ReportedBinding, Overlaping);
});
}

static mcdxbc::RootSignatureDesc *
getRootSignature(RootSignatureBindingInfo &RSBI,
dxil::ModuleMetadataInfo &MMI) {
if (MMI.EntryPropertyVec.size() == 0)
return nullptr;
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
assert(MMI.EntryPropertyVec.size() == 1 && ...)
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);

Is that true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, there are a lot of tests that compile without an entry point, mainly scenarios for libraries.

Copy link
Contributor

Choose a reason for hiding this comment

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

But if it isn't 0, is it then always 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, HLSL can have multiple entry point, for multiple shader stages.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case, what makes it always be the first one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For non library shader compilation, only one entry point is expected. If we have more than one entry point, we are compiling a library shader. Library don't compile root signature to dxcontainer.

Copy link
Contributor

Choose a reason for hiding this comment

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

We really need to clean up the places that make this assumption and grab the first entry of the entry point vector explicitly, either by wrapping it with an API with an explicit name or by more explicitly handling library profiles. That said, this is a pretty ubiquitous problem already I don't think we need to fix it in this PR.

}

static void reportErrors(Module &M, DXILResourceMap &DRM,
DXILResourceBindingInfo &DRBI) {
DXILResourceBindingInfo &DRBI,
RootSignatureBindingInfo &RSBI,
dxil::ModuleMetadataInfo &MMI) {
if (DRM.hasInvalidCounterDirection())
reportInvalidDirection(M, DRM);

Expand All @@ -94,14 +243,19 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,

assert(!DRBI.hasImplicitBinding() && "implicit bindings should be handled in "
"DXILResourceImplicitBinding pass");

if (mcdxbc::RootSignatureDesc *RSD = getRootSignature(RSBI, MMI))
validateRootSignature(M, *RSD, MMI);
}
} // namespace

PreservedAnalyses
DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {
DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
DXILResourceBindingInfo &DRBI = MAM.getResult<DXILResourceBindingAnalysis>(M);
reportErrors(M, DRM, DRBI);
RootSignatureBindingInfo &RSBI = MAM.getResult<RootSignatureAnalysis>(M);
ModuleMetadataInfo &MMI = MAM.getResult<DXILMetadataAnalysis>(M);

reportErrors(M, DRM, DRBI, RSBI, MMI);
return PreservedAnalyses::all();
}

Expand All @@ -113,7 +267,12 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
getAnalysis<DXILResourceWrapperPass>().getResourceMap();
DXILResourceBindingInfo &DRBI =
getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
reportErrors(M, DRM, DRBI);
RootSignatureBindingInfo &RSBI =
getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
dxil::ModuleMetadataInfo &MMI =
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();

reportErrors(M, DRM, DRBI, RSBI, MMI);
return false;
}
StringRef getPassName() const override {
Expand All @@ -125,10 +284,13 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
AU.addRequired<DXILResourceWrapperPass>();
AU.addRequired<DXILResourceBindingWrapperPass>();
AU.addRequired<DXILMetadataAnalysisWrapperPass>();
AU.addRequired<RootSignatureAnalysisWrapper>();
AU.addPreserved<DXILResourceWrapperPass>();
AU.addPreserved<DXILResourceBindingWrapperPass>();
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
AU.addPreserved<ShaderFlagsAnalysisWrapper>();
AU.addPreserved<RootSignatureAnalysisWrapper>();
}
};
char DXILPostOptimizationValidationLegacy::ID = 0;
Expand All @@ -139,6 +301,8 @@ INITIALIZE_PASS_BEGIN(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)
INITIALIZE_PASS_END(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
"DXIL Post Optimization Validation", false, false)

Expand Down
8 changes: 3 additions & 5 deletions llvm/lib/Target/DirectX/DXILRootSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ class RootSignatureBindingInfo {

iterator end() { return FuncToRsMap.end(); }

std::optional<mcdxbc::RootSignatureDesc>
getDescForFunction(const Function *F) {
mcdxbc::RootSignatureDesc *getDescForFunction(const Function *F) {
const auto FuncRs = find(F);
if (FuncRs == end())
return std::nullopt;

return FuncRs->second;
return nullptr;
return &FuncRs->second;
}
};

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/llc-pipeline.ll
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
; CHECK-NEXT: DXIL Module Metadata analysis
; CHECK-NEXT: DXIL Shader Flag Analysis
; CHECK-NEXT: DXIL Translate Metadata
; CHECK-NEXT: DXIL Root Signature Analysis
; CHECK-NEXT: DXIL Post Optimization Validation
; CHECK-NEXT: DXIL Op Lowering
; CHECK-NEXT: DXIL Root Signature Analysis
; CHECK-NEXT: DXIL Prepare Module

; CHECK-ASM-NEXT: DXIL Metadata Pretty Printer
Expand Down
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/DirectX/rootsignature-validation-constants.ll
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 this really adds much value by itself, maybe we can move it to the rootsignature-validation.ll to show it doesn't overlap with a CBV range there.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1
; expected-no-diagnostics
; Root Signature(RootConstants(num32BitConstants=4, b2))

define void @CSMain() "hlsl.shader"="compute" {
entry:
ret void
}

!dx.rootsignatures = !{!0}

!0 = !{ptr @CSMain, !1, i32 2}
!1 = !{!2}
!2 = !{!"RootConstants", i32 0, i32 2, i32 0, i32 4}
Loading
Loading