diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index e89a03741d..a92f9258a3 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -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 diff --git a/lib/DxilValidation/DxilValidationUtils.cpp b/lib/DxilValidation/DxilValidationUtils.cpp index f0f85fcd26..8caeb6f83a 100644 --- a/lib/DxilValidation/DxilValidationUtils.cpp +++ b/lib/DxilValidation/DxilValidationUtils.cpp @@ -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" @@ -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(U); + DxilInst_CreateHandleFromBinding Hdl(CI); + + // Validate bind parameter is constant. + Value *Bind = Hdl.get_bind(); + if (!isa(Bind)) { + EmitInstrError(CI, ValidationRule::InstrOpConstRange); + continue; + } + + DxilResourceBinding B = + resource_helper::loadBindingFromCreateHandleFromBinding( + Hdl, hlslOP->GetHandleType(), SM); + + // Validate resourceClass is valid. + switch (static_cast(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(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) diff --git a/tools/clang/test/LitDXILValidation/createHandleFromBinding_failing.ll b/tools/clang/test/LitDXILValidation/createHandleFromBinding_failing.ll new file mode 100644 index 0000000000..3d45b06e9b --- /dev/null +++ b/tools/clang/test/LitDXILValidation/createHandleFromBinding_failing.ll @@ -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} diff --git a/tools/clang/test/LitDXILValidation/createHandleFromBinding_non_constant_bind.ll b/tools/clang/test/LitDXILValidation/createHandleFromBinding_non_constant_bind.ll new file mode 100644 index 0000000000..4297c6daad --- /dev/null +++ b/tools/clang/test/LitDXILValidation/createHandleFromBinding_non_constant_bind.ll @@ -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} diff --git a/tools/clang/test/LitDXILValidation/createHandleFromBinding_passing.ll b/tools/clang/test/LitDXILValidation/createHandleFromBinding_passing.ll new file mode 100644 index 0000000000..70135cb470 --- /dev/null +++ b/tools/clang/test/LitDXILValidation/createHandleFromBinding_passing.ll @@ -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}