Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e0ec755
Initial plan
Copilot Feb 21, 2026
d0d701c
Fix createHandleFromBinding index validation and add test (issue #7019)
Copilot Feb 21, 2026
c780148
Add test for index above upper bound in createHandleFromBinding valid…
Copilot Feb 21, 2026
5dc365f
Add release note for CreateHandleFromBinding index validation fix
Copilot Feb 23, 2026
da8d8c3
Update release note to identify DXIL validation change
Copilot Feb 23, 2026
9f17031
Verify code format changes come from the github-actions bot
damyanp Mar 10, 2026
3c5196f
Merge remote-tracking branch 'origin/main' into copilot/fix-issue-701…
Copilot Mar 11, 2026
41a15f9
Add comprehensive validation for CreateHandleFromBinding
Copilot Mar 11, 2026
3b74288
Fix resourceClass comment in test to match enum values
Copilot Mar 11, 2026
7dd68e5
Merge branch 'main' of https://github.com/damyanp/DirectXShaderCompil…
Copilot Mar 12, 2026
ca0336e
Update release note wording and add test for non-constant bind parameter
Copilot Mar 12, 2026
99c3cc3
Restructure tests into passing/failing pair, drop non-constant bind test
Copilot Mar 12, 2026
60dd05d
Restore non-constant bind test as third test file
Copilot Mar 12, 2026
57301f7
Fix inaccurate comment in createHandleFromBinding_failing.ll
Copilot Mar 13, 2026
0d38ccf
Reject non-constant index for non-array CreateHandleFromBinding
damyanp Mar 13, 2026
3562ff9
Revert non-array index check; keep array non-constant index test
damyanp Mar 13, 2026
d815050
Apply LLVM naming conventions to CreateHandleFromBinding validation
damyanp Mar 13, 2026
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
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ line upon naming the release. Refer to previous for appropriate section names.

- Fixed non-deterministic DXIL/PDB output when compiling shaders with resource arrays, debug info, and SM 6.6+. [#8171](https://github.com/microsoft/DirectXShaderCompiler/issues/8171)
- Fixed mesh shader semantics that were incorrectly case sensitive.
- DXIL validation: added validation for `CreateHandleFromBinding`.
- DXIL validation now rejects non-standard integer bit widths (e.g. `i25`) in instructions.

### Version 1.9.2602
Expand Down
47 changes: 47 additions & 0 deletions lib/DxilValidation/DxilValidationUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilResourceBinding.h"
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/Support/Global.h"

Expand Down Expand Up @@ -229,6 +230,52 @@ void ValidationContext::BuildResMap() {
}
const ShaderModel &SM = *DxilMod.GetShaderModel();

// Scan all createHandleFromBinding for validation.
for (auto &it :
hlslOP->GetOpFuncList(DXIL::OpCode::CreateHandleFromBinding)) {
Function *F = it.second;
if (!F)
continue;
for (User *U : F->users()) {
CallInst *CI = cast<CallInst>(U);
DxilInst_CreateHandleFromBinding Hdl(CI);

// Validate bind parameter is constant.
Value *Bind = Hdl.get_bind();
if (!isa<Constant>(Bind)) {
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
continue;
}

DxilResourceBinding B =
resource_helper::loadBindingFromCreateHandleFromBinding(
Hdl, hlslOP->GetHandleType(), SM);

// Validate resourceClass is valid.
switch (static_cast<DXIL::ResourceClass>(B.resourceClass)) {
case DXIL::ResourceClass::CBuffer:
case DXIL::ResourceClass::Sampler:
case DXIL::ResourceClass::SRV:
case DXIL::ResourceClass::UAV:
break;
default:
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
continue;
}

// Validate constant index is within binding range.
ConstantInt *CIndex = dyn_cast<ConstantInt>(Hdl.get_index());
if (CIndex) {
unsigned Index = CIndex->getLimitedValue();
if (Index < B.rangeLowerBound || Index > B.rangeUpperBound) {
// Index out of range.
EmitInstrError(CI, ValidationRule::InstrOpConstRange);
continue;
}
}
}
}

for (auto &it : hlslOP->GetOpFuncList(DXIL::OpCode::AnnotateHandle)) {
Function *F = it.second;
if (!F)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
; REQUIRES: dxil-1-8
; RUN: not %dxv %s 2>&1 | FileCheck %s

; Verify that createHandleFromBinding rejects out-of-range indices
; and invalid resource classes.

target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"

%dx.types.Handle = type { i8* }
%dx.types.ResBind = type { i32, i32, i32, i8 }
%dx.types.ResourceProperties = type { i32, i32 }
%struct.RWByteAddressBuffer = type { i32 }

; --- Index below lower bound ---
; CHECK-DAG: error: Constant values must be in-range for operation.
; CHECK-DAG: note: at '%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 0, i1 false)' in block '#0' of function 'main'.

; --- Index above upper bound ---
; CHECK-DAG: error: Constant values must be in-range for operation.
; CHECK-DAG: note: at '%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 4, i1 false)' in block '#0' of function 'main'.

; --- Invalid resource class ---
; CHECK-DAG: error: Constant values must be in-range for operation.
; CHECK-DAG: note: at '%5 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 5 }, i32 0, i1 false)' in block '#0' of function 'main'.

define void @main() {
; Index 0 is below rangeLowerBound=1
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 0, i1 false)
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4107, i32 0 })
; Index 4 is above rangeUpperBound=3
%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 4, i1 false)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4107, i32 0 })
; resourceClass=5 is invalid (valid: 0=SRV, 1=UAV, 2=CBuffer, 3=Sampler)
%5 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 5 }, i32 0, i1 false)
%6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 4107, i32 0 })
ret void
}

declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0

attributes #0 = { nounwind readnone }

!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.entryPoints = !{!6}

!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
!1 = !{i32 1, i32 8}
!2 = !{!"cs", i32 6, i32 8}
!3 = !{null, !4, null, null}
!4 = !{!5}
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 1, i32 3, i32 11, i1 false, i1 false, i1 false, null}
!6 = !{void ()* @main, !"main", null, !3, !7}
!7 = !{i32 0, i64 8589934608, i32 4, !8}
!8 = !{i32 4, i32 1, i32 1}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
; REQUIRES: dxil-1-8
; RUN: not %dxv %s 2>&1 | FileCheck %s

target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"

%dx.types.Handle = type { i8* }
%dx.types.ResBind = type { i32, i32, i32, i8 }
%dx.types.ResourceProperties = type { i32, i32 }
%struct.RWByteAddressBuffer = type { i32 }

; CHECK: error: Constant values must be in-range for operation.
; CHECK: note: at '%2 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind %1,

define void @main(i32 %lowerBound) {
; Construct a non-constant bind struct using a function parameter
%1 = insertvalue %dx.types.ResBind { i32 undef, i32 1, i32 0, i8 1 }, i32 %lowerBound, 0
; Pass non-constant bind to createHandleFromBinding — should fail validation
%2 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind %1, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
%3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 4107, i32 0 }) ; AnnotateHandle(res,props) resource: RWByteAddressBuffer
ret void
}

; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0

; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0

attributes #0 = { nounwind readnone }

!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.entryPoints = !{!6}

!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
!1 = !{i32 1, i32 8}
!2 = !{!"cs", i32 6, i32 8}
!3 = !{null, !4, null, null}
!4 = !{!5}
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 0, i32 1, i32 11, i1 false, i1 false, i1 false, null}
!6 = !{void (i32)* @main, !"main", null, !3, !7}
!7 = !{i32 0, i64 8589934608, i32 4, !8}
!8 = !{i32 4, i32 1, i32 1}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; REQUIRES: dxil-1-8
; RUN: %dxv %s | FileCheck %s

; CHECK: Validation succeeded.

; Verify that valid createHandleFromBinding calls pass DXIL validation:
; in-range constant indices, valid resource classes, and non-constant
; indices on array resources.

target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"

%dx.types.Handle = type { i8* }
%dx.types.ResBind = type { i32, i32, i32, i8 }
%dx.types.ResourceProperties = type { i32, i32 }
%struct.RWByteAddressBuffer = type { i32 }

define void @main() {
; Valid: index 1 is within [1, 3], resourceClass=1 (UAV)
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 1, i1 false)
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4107, i32 0 })
; Valid: index 3 is within [1, 3] (upper bound)
%3 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 3, i1 false)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4107, i32 0 })
; Valid: non-constant index on array resource (range [1, 3])
%5 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)
%6 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 1, i32 3, i32 0, i8 1 }, i32 %5, i1 false)
%7 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %6, %dx.types.ResourceProperties { i32 4107, i32 0 })
ret void
}

declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #0
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #0

attributes #0 = { nounwind readnone }

!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.entryPoints = !{!6}

!0 = !{!"dxc(private) 1.8.0.15017 (main, 4e0f5364a-dirty)"}
!1 = !{i32 1, i32 8}
!2 = !{!"cs", i32 6, i32 8}
!3 = !{null, !4, null, null}
!4 = !{!5}
!5 = !{i32 0, %struct.RWByteAddressBuffer* undef, !"", i32 0, i32 1, i32 3, i32 11, i1 false, i1 false, i1 false, null}
!6 = !{void ()* @main, !"main", null, !3, !7}
!7 = !{i32 0, i64 8589934608, i32 4, !8}
!8 = !{i32 4, i32 1, i32 1}
Loading