[val] Add missing IR validator tests 2/5 This CL adds missing testing coverage to branches related to to functions and built-in/attributes validation, e.g. "root block for function is undefined" or "instruction for @workgroup_size param is not defined". Bug: 502244655 Skip-Clang-Tidy-Checks: readability-trailing-comma Change-Id: I72fab6e66d2e80d38f64153a39310e270849e968 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/302915 Auto-Submit: Ryan Harrison <rharrison@chromium.org> Reviewed-by: dan sinclair <dsinclair@chromium.org> Reviewed-by: James Price <jrprice@google.com> Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc index a0ee08f..5bf90ed 100644 --- a/src/tint/lang/core/ir/validator.cc +++ b/src/tint/lang/core/ir/validator.cc
@@ -2651,10 +2651,10 @@ return; } - if (!func->Block()) { - AddError(func) << "root block for function is undefined"; - return; - } + // Note: This is not a validator error because Function::SetBlock() asserts that the block is + // not null, and the disassembler will crash if this is not null. This should only be hit due + // to some sort of corruption, not a bad shader/programmer error. + TINT_ASSERT(func->Block()) << "root block for function is undefined"; if (func->Block()->Is<ir::MultiInBlock>()) { AddError(func) << "root block for function cannot be a multi-in block";
diff --git a/src/tint/lang/core/ir/validator_builtin_test.cc b/src/tint/lang/core/ir/validator_builtin_test.cc index 9cb5bd6..ee09e851 100644 --- a/src/tint/lang/core/ir/validator_builtin_test.cc +++ b/src/tint/lang/core/ir/validator_builtin_test.cc
@@ -1332,6 +1332,23 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, InputAttachmentIndex_NonEntryPoint_InvalidIOKind) { + auto* f = b.Function("my_func", ty.void_()); + auto* p = b.FunctionParam("p", ty.input_attachment(ty.f32())); + p->SetAttributes(IOAttributes{.input_attachment_index = 0u}); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(input attachment index IO attributes cannot be declared on a input param. They can only be used on a module scope variable.)")) + << res.Failure(); +} + namespace { template <typename T> static const core::type::Type* TypeBuilder(core::type::Manager& m) {
diff --git a/src/tint/lang/core/ir/validator_function_test.cc b/src/tint/lang/core/ir/validator_function_test.cc index 8ad32b1..116f393 100644 --- a/src/tint/lang/core/ir/validator_function_test.cc +++ b/src/tint/lang/core/ir/validator_function_test.cc
@@ -35,6 +35,7 @@ #include "src/tint/lang/core/number.h" #include "src/tint/lang/core/type/abstract_float.h" #include "src/tint/lang/core/type/abstract_int.h" +#include "src/tint/lang/core/type/function.h" #include "src/tint/lang/core/type/manager.h" #include "src/tint/lang/core/type/matrix.h" #include "src/tint/lang/core/type/memory_view.h" @@ -480,6 +481,41 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, ValidateShaderIOAnnotations_Location_NonNumeric) { + auto* f = FragmentEntryPoint("my_func"); + auto* p = b.FunctionParam("p", ty.bool_()); + p->SetAttributes(IOAttributes{.location = 0u}); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr("error: input param with a location attribute must be a numeric " + "scalar or vector, but has type bool")) + << res.Failure(); +} + +TEST_F(IR_ValidatorTest, ValidateShaderIOAnnotations_StructMember_Location_NonNumeric) { + auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), + { + {mod.symbols.New("a"), ty.bool_(), IOAttributes{.location = 0u}}, + }); + auto* f = FragmentEntryPoint("my_func"); + auto* p = b.FunctionParam("p", str_ty); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr("error: input param struct member with a location attribute " + "must be a numeric scalar or vector, but has type bool")) + << res.Failure(); +} + TEST_F(IR_ValidatorTest, EntryPoint_InputLocation_Duplicate_InParams) { auto* f = FragmentEntryPoint("my_func"); @@ -1387,6 +1423,27 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_Interpolate_WithoutLocation_LoosenValidation) { + auto* f = FragmentEntryPoint("my_func"); + + auto* p = b.FunctionParam("p", ty.f32()); + p->SetInterpolation(Interpolation{.type = InterpolationType::kLinear, + .sampling = InterpolationSampling::kCenter}); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kLoosenValidationForShaderIO}); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(:1:27 error: interpolation attribute requires a location attribute (or location-like shader I/O annotation) +%my_func = @fragment func(%p:f32):void { + ^^^^^^ +)")) << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_Interpolate_Struct_WithLocation) { auto* f = FragmentEntryPoint("my_func"); @@ -1890,6 +1947,66 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_Param_BindingPointWithoutCapability) { + auto* f = b.Function("my_func", ty.void_()); + auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); + p->SetBindingPoint(0, 0); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr( + R"(:1:17 error: input param to non-entry point function has a binding point set +%my_func = func(%my_param:ptr<uniform, i32, read> [@binding_point(0, 0)]):void { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +)")) << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Function_EntryPointParam_BindingPointWithoutCapability) { + auto* f = ComputeEntryPoint("my_func"); + auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); + p->SetBindingPoint(0, 0); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr( + R"(:1:54 error: binding_points are only valid on resource variables +%my_func = @compute @workgroup_size(1u, 1u, 1u) func(%my_param:ptr<uniform, i32, read> [@binding_point(0, 0)]):void { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +)")) << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Function_EntryPointParam_BindingPointWithCapability) { + auto* f = ComputeEntryPoint("my_func"); + auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); + p->SetBindingPoint(0, 0); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kMslAllowEntryPointInterface}); + ASSERT_EQ(res, Success) << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Function_Param_Color_F16) { + auto* f = FragmentEntryPoint("my_func"); + auto* p = b.FunctionParam("my_param", ty.f16()); + p->SetColor(0); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes}); + EXPECT_EQ(res, Success); +} + TEST_F(IR_ValidatorTest, Function_Param_Color_NonFragment) { auto* f = b.ComputeFunction("my_func"); auto* p = b.FunctionParam("my_param", ty.vec4f()); @@ -1935,104 +2052,6 @@ )")) << res.Failure(); } -TEST_F(IR_ValidatorTest, Function_Return_Color) { - auto* f = FragmentEntryPoint("my_func"); - f->SetReturnType(ty.vec4f()); - - IOAttributes attr; - attr.color = 0; - f->SetReturnAttributes(attr); - - b.Append(f->Block(), [&] { b.Return(f, b.Zero(ty.vec4f())); }); - - auto res = ir::Validate(mod); - ASSERT_NE(res, Success); - EXPECT_THAT( - res.Failure().reason, - testing::HasSubstr( - R"(:1:1 error: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. -%my_func = @fragment func():vec4<f32> { -^^^^^^^^ -)")) << res.Failure(); -} - -TEST_F(IR_ValidatorTest, Function_Return_Struct_Color) { - IOAttributes attr; - attr.color = 0; - - auto* str_ty = - ty.Struct(mod.symbols.New("MyStruct"), { - {mod.symbols.New("pos"), ty.vec4f(), attr}, - }); - - auto* f = FragmentEntryPoint("my_func"); - f->SetReturnType(str_ty); - - b.Append(f->Block(), [&] { b.Return(f, b.Zero(str_ty)); }); - - auto res = ir::Validate(mod); - ASSERT_NE(res, Success); - EXPECT_THAT( - res.Failure().reason, - testing::HasSubstr( - R"(:5:1 error: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. -%my_func = @fragment func():MyStruct { -^^^^^^^^ -)")) << res.Failure(); -} - -TEST_F(IR_ValidatorTest, Function_MSV_Color_Output) { - auto* f = FragmentEntryPoint("my_func"); - - auto* v = b.Var("v", AddressSpace::kOut, ty.vec4f()); - v->SetColor(0); - mod.root_block->Append(v); - - b.Append(f->Block(), [&] { - b.Store(v, b.Zero(ty.vec4f())); - b.Return(f); - }); - - auto res = ir::Validate(mod); - ASSERT_NE(res, Success); - EXPECT_THAT( - res.Failure().reason, - testing::HasSubstr( - R"(:2:42 error: var: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. - %v:ptr<__out, vec4<f32>, read_write> = var undef - ^^^ -)")) << res.Failure(); -} - -TEST_F(IR_ValidatorTest, Function_MSV_Struct_Color_Output) { - auto* f = FragmentEntryPoint("my_func"); - - IOAttributes attr; - attr.color = 0; - auto* str_ty = - ty.Struct(mod.symbols.New("MyStruct"), { - {mod.symbols.New("col"), ty.vec4f(), attr}, - }); - - auto* v = b.Var("v", AddressSpace::kOut, str_ty); - mod.root_block->Append(v); - - b.Append(f->Block(), [&] { - b.Store(v, b.Zero(str_ty)); - b.Return(f); - }); - - auto res = ir::Validate(mod); - ASSERT_NE(res, Success); - EXPECT_THAT( - res.Failure().reason, - testing::HasSubstr( - R"(:6:41 error: var: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. - %v:ptr<__out, MyStruct, read_write> = var undef - ^^^ -)")) << res.Failure(); -} - TEST_F(IR_ValidatorTest, Function_MSV_Color_Input_Fragment) { auto* f = FragmentEntryPoint("my_func"); @@ -2122,52 +2141,100 @@ )")) << res.Failure(); } -TEST_F(IR_ValidatorTest, Function_Param_BindingPointWithoutCapability) { - auto* f = b.Function("my_func", ty.void_()); - auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); - p->SetBindingPoint(0, 0); - f->SetParams({p}); +TEST_F(IR_ValidatorTest, Function_Return_Color) { + auto* f = FragmentEntryPoint("my_func"); + f->SetReturnType(ty.vec4f()); - b.Append(f->Block(), [&] { b.Return(f); }); + IOAttributes attr; + attr.color = 0; + f->SetReturnAttributes(attr); + + b.Append(f->Block(), [&] { b.Return(f, b.Zero(ty.vec4f())); }); auto res = ir::Validate(mod); ASSERT_NE(res, Success); - EXPECT_THAT(res.Failure().reason, - testing::HasSubstr( - R"(:1:17 error: input param to non-entry point function has a binding point set -%my_func = func(%my_param:ptr<uniform, i32, read> [@binding_point(0, 0)]):void { - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(:1:1 error: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. +%my_func = @fragment func():vec4<f32> { +^^^^^^^^ )")) << res.Failure(); } -TEST_F(IR_ValidatorTest, Function_EntryPointParam_BindingPointWithoutCapability) { - auto* f = ComputeEntryPoint("my_func"); - auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); - p->SetBindingPoint(0, 0); - f->SetParams({p}); +TEST_F(IR_ValidatorTest, Function_Return_Struct_Color) { + IOAttributes attr; + attr.color = 0; - b.Append(f->Block(), [&] { b.Return(f); }); + auto* str_ty = + ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("pos"), ty.vec4f(), attr}, + }); + + auto* f = FragmentEntryPoint("my_func"); + f->SetReturnType(str_ty); + + b.Append(f->Block(), [&] { b.Return(f, b.Zero(str_ty)); }); auto res = ir::Validate(mod); ASSERT_NE(res, Success); - EXPECT_THAT(res.Failure().reason, - testing::HasSubstr( - R"(:1:54 error: binding_points are only valid on resource variables -%my_func = @compute @workgroup_size(1u, 1u, 1u) func(%my_param:ptr<uniform, i32, read> [@binding_point(0, 0)]):void { - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(:5:1 error: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. +%my_func = @fragment func():MyStruct { +^^^^^^^^ )")) << res.Failure(); } -TEST_F(IR_ValidatorTest, Function_EntryPointParam_BindingPointWithCapability) { - auto* f = ComputeEntryPoint("my_func"); - auto* p = b.FunctionParam("my_param", ty.ptr<uniform, i32>()); - p->SetBindingPoint(0, 0); - f->SetParams({p}); +TEST_F(IR_ValidatorTest, Function_MSV_Color_Output) { + auto* f = FragmentEntryPoint("my_func"); - b.Append(f->Block(), [&] { b.Return(f); }); + auto* v = b.Var("v", AddressSpace::kOut, ty.vec4f()); + v->SetColor(0); + mod.root_block->Append(v); - auto res = ir::Validate(mod, Capabilities{Capability::kMslAllowEntryPointInterface}); - ASSERT_EQ(res, Success) << res.Failure(); + b.Append(f->Block(), [&] { + b.Store(v, b.Zero(ty.vec4f())); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(error: var: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input.)")) + << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Function_MSV_Struct_Color_Output) { + auto* f = FragmentEntryPoint("my_func"); + + IOAttributes attr; + attr.color = 0; + auto* str_ty = + ty.Struct(mod.symbols.New("MyStruct"), { + {mod.symbols.New("col"), ty.vec4f(), attr}, + }); + + auto* v = b.Var("v", AddressSpace::kOut, str_ty); + mod.root_block->Append(v); + + b.Append(f->Block(), [&] { + b.Store(v, b.Zero(str_ty)); + b.Return(f); + }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr( + R"(:6:41 error: var: color IO attributes cannot be declared for a fragment shader output. They can only be used for a fragment shader input. + %v:ptr<__out, MyStruct, read_write> = var undef + ^^^ +)")) << res.Failure(); } TEST_F(IR_ValidatorTest, Function_Param_Color_InvalidType) { @@ -2188,6 +2255,23 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_Param_Color_Bool) { + auto* f = FragmentEntryPoint("my_func"); + auto* p = b.FunctionParam("my_param", ty.bool_()); + p->SetColor(0); + f->SetParams({p}); + + b.Append(f->Block(), [&] { b.Return(f); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, testing::HasSubstr( + R"(:1:27 error: color must be a numeric scalar or vector +%my_func = @fragment func(%my_param:bool [@color(0)]):void { + ^^^^^^^^^^^^^^ +)")) << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_Param_InputIndexAttachment) { auto* f = FragmentEntryPoint("my_func"); @@ -2623,6 +2707,19 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_WorkgroupSize_InstructionNotDefined) { + auto* ep = b.ComputeFunction("ep"); + auto* res_val = mod.CreateValue<ir::InstructionResult>(ty.u32()); + ep->SetWorkgroupSize({res_val, b.Constant(1_u), b.Constant(1_u)}); + b.Append(ep->Block(), [&] { b.Return(ep); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kAllowOverrides}); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr("error: instruction for @workgroup_size param is not defined")) + << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_WorkgroupSize_ParamWrongType) { auto* f = ComputeEntryPoint(); f->SetWorkgroupSize({b.Constant(1_f), b.Constant(2_u), b.Constant(3_u)}); @@ -2655,6 +2752,19 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_WorkgroupSize_InvalidValue) { + auto* ep = b.ComputeFunction("ep"); + ep->SetWorkgroupSize({b.FunctionParam(ty.u32()), b.Constant(1_u), b.Constant(1_u)}); + b.Append(ep->Block(), [&] { b.Return(ep); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kAllowOverrides}); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr("error: @workgroup_size must be an InstructionResult or a Constant")) + << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_WorkgroupSize_InvalidValueKind) { auto* f = ComputeEntryPoint(); f->SetWorkgroupSize({b.Constant(1_u), b.FunctionParam("p", ty.u32()), b.Constant(3_u)}); @@ -2826,6 +2936,33 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_SubgroupSize_MissingType) { + auto* ep = b.ComputeFunction("ep"); + auto* val = b.Constant(1_u); + val->SetType(nullptr); + ep->SetSubgroupSize(val); + b.Append(ep->Block(), [&] { b.Return(ep); }); + + auto res = ir::Validate(mod); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr("error: a @subgroup_size param is missing a type")) + << res.Failure(); +} + +TEST_F(IR_ValidatorTest, Function_SubgroupSize_InstructionNotDefined) { + auto* ep = b.ComputeFunction("ep"); + auto* res_val = mod.CreateValue<ir::InstructionResult>(ty.u32()); + ep->SetSubgroupSize(res_val); + b.Append(ep->Block(), [&] { b.Return(ep); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kAllowOverrides}); + ASSERT_NE(res, Success); + EXPECT_THAT(res.Failure().reason, + testing::HasSubstr("error: instruction for @subgroup_size param is not defined")) + << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_SubgroupSize_ParamWrongType) { auto* f = ComputeEntryPoint(); f->SetWorkgroupSize({b.Constant(1_u), b.Constant(2_u), b.Constant(3_u)}); @@ -2875,6 +3012,19 @@ )")) << res.Failure(); } +TEST_F(IR_ValidatorTest, Function_SubgroupSize_InvalidValue) { + auto* ep = b.ComputeFunction("ep"); + ep->SetSubgroupSize(b.FunctionParam(ty.u32())); + b.Append(ep->Block(), [&] { b.Return(ep); }); + + auto res = ir::Validate(mod, Capabilities{Capability::kAllowOverrides}); + ASSERT_NE(res, Success); + EXPECT_THAT( + res.Failure().reason, + testing::HasSubstr("error: @subgroup_size must be an InstructionResult or a Constant")) + << res.Failure(); +} + TEST_F(IR_ValidatorTest, Function_SubgroupSize_ParamNonPowerOfTwo) { auto* f = ComputeEntryPoint(); f->SetWorkgroupSize({b.Constant(1_u), b.Constant(2_u), b.Constant(3_u)}); @@ -3239,33 +3389,4 @@ )")) << res.Failure(); } -TEST_F(IR_ValidatorTest, Function_Param_Color_F16) { - auto* f = FragmentEntryPoint("my_func"); - auto* p = b.FunctionParam("my_param", ty.f16()); - p->SetColor(0); - f->SetParams({p}); - - b.Append(f->Block(), [&] { b.Return(f); }); - - auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes}); - EXPECT_EQ(res, Success); -} - -TEST_F(IR_ValidatorTest, Function_Param_Color_Bool) { - auto* f = FragmentEntryPoint("my_func"); - auto* p = b.FunctionParam("my_param", ty.bool_()); - p->SetColor(0); - f->SetParams({p}); - - b.Append(f->Block(), [&] { b.Return(f); }); - - auto res = ir::Validate(mod); - ASSERT_NE(res, Success); - EXPECT_THAT(res.Failure().reason, testing::HasSubstr( - R"(:1:27 error: color must be a numeric scalar or vector -%my_func = @fragment func(%my_param:bool [@color(0)]):void { - ^^^^^^^^^^^^^^ -)")) << res.Failure(); -} - } // namespace tint::core::ir