[tint][ir][val] Check position present if invariant is
Fixes: 369788643
Change-Id: I28ce895fceee1bc5a1cbeea78321cb454df4b172
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/209195
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 8c6ad6d..c9ab7c1 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -134,6 +134,12 @@
return attr.builtin.has_value() && attr.location.has_value();
}
+/// @return true if @param attr does not have invariant decoration or if it also has position
+/// decoration
+bool InvariantOnlyIfAlsoPosition(const tint::core::IOAttributes& attr) {
+ return !attr.invariant || attr.builtin == BuiltinValue::kPosition;
+}
+
/// @returns true if @p ty meets the basic function parameter rules (i.e. one of constructible,
/// pointer, sampler or texture).
///
@@ -1191,6 +1197,11 @@
param->Type(), [&]() -> diag::Diagnostic& { return AddError(param); },
Capabilities{Capability::kAllowRefTypes});
+ if (!InvariantOnlyIfAlsoPosition(param->Attributes())) {
+ AddError(func)
+ << "invariant can only decorate a param iff it is also decorated with position";
+ }
+
if (!IsValidFunctionParamType(param->Type())) {
auto struct_ty = param->Type()->As<core::type::Struct>();
if (!capabilities_.Contains(Capability::kAllowPointersInStructures) || !struct_ty ||
@@ -1210,6 +1221,11 @@
if (auto* s = param->Type()->As<core::type::Struct>()) {
for (auto* mem : s->Members()) {
+ if (!InvariantOnlyIfAlsoPosition(mem->Attributes())) {
+ AddError(func) << "invariant can only decorate a param member iff it is also "
+ "decorated with position";
+ }
+
if (HasLocationAndBuiltin(mem->Attributes())) {
AddError(param)
<< "a builtin and location cannot be both declared for a struct member";
@@ -1264,6 +1280,21 @@
AddError(func) << "function return type must be constructible";
}
+ const auto* ret_struct = func->ReturnType()->As<core::type::Struct>();
+ if (ret_struct) {
+ for (auto* mem : ret_struct->Members()) {
+ if (!InvariantOnlyIfAlsoPosition(mem->Attributes())) {
+ AddError(func) << "invariant can only decorate a member iff it is also decorated "
+ "with position";
+ }
+ }
+ } else {
+ if (!InvariantOnlyIfAlsoPosition(func->ReturnAttributes())) {
+ AddError(func)
+ << "invariant can only decorate a return iff it is also decorated with position";
+ }
+ }
+
if (func->Stage() != Function::PipelineStage::kFragment) {
if (DAWN_UNLIKELY(func->ReturnBuiltin().has_value() &&
func->ReturnBuiltin().value() == BuiltinValue::kFragDepth)) {
@@ -1273,6 +1304,7 @@
if (func->Stage() == Function::PipelineStage::kVertex) {
CheckVertexEntryPoint(func);
+ } else {
}
QueueBlock(func->Block());
@@ -1284,6 +1316,11 @@
bool contains_position = false;
if (ret_struct) {
for (auto* mem : ret_struct->Members()) {
+ if (!InvariantOnlyIfAlsoPosition(mem->Attributes())) {
+ AddError(ep) << "invariant can only decorate output members iff they are also "
+ "position builtins";
+ }
+
if (!mem->Attributes().builtin.has_value()) {
continue;
}
@@ -1300,6 +1337,10 @@
}
}
} else {
+ if (!InvariantOnlyIfAlsoPosition(ep->ReturnAttributes())) {
+ AddError(ep)
+ << "invariant can only decorate outputs iff they are also position builtins";
+ }
if (ep->ReturnBuiltin() && ep->ReturnBuiltin() == BuiltinValue::kPosition) {
contains_position = true;
CheckBuiltinPosition(ep, ep->ReturnType());
@@ -1311,6 +1352,11 @@
const auto* res_struct = res_type->As<core::type::Struct>();
if (res_struct) {
for (auto* mem : res_struct->Members()) {
+ if (!InvariantOnlyIfAlsoPosition(mem->Attributes())) {
+ AddError(ep) << "invariant can only decorate members iff they are also "
+ "position builtins";
+ }
+
if (!mem->Attributes().builtin.has_value()) {
continue;
}
@@ -1327,6 +1373,11 @@
}
}
} else {
+ if (!InvariantOnlyIfAlsoPosition(var->Attributes())) {
+ AddError(ep)
+ << "invariant can only decorate vars iff they are also position builtins";
+ }
+
if (!var->Attributes().builtin.has_value()) {
continue;
}
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 33bbf50..80bc71c 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -446,6 +446,104 @@
)");
}
+TEST_F(IR_ValidatorTest, Function_Param_InvariantWithPosition) {
+ auto* f = b.Function("my_func", ty.void_());
+ auto* p = b.FunctionParam("my_param", ty.vec4<f32>());
+ IOAttributes attr;
+ attr.builtin = BuiltinValue::kPosition;
+ attr.invariant = true;
+ p->SetAttributes(attr);
+ f->SetParams({p});
+
+ b.Append(f->Block(), [&] { b.Return(f); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Function_Param_InvariantWithoutPosition) {
+ auto* f = b.Function("my_func", ty.void_());
+ auto* p = b.FunctionParam("my_param", ty.vec4<f32>());
+ IOAttributes attr;
+ attr.invariant = true;
+ p->SetAttributes(attr);
+ f->SetParams({p});
+
+ b.Append(f->Block(), [&] { b.Return(f); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:1:1 error: invariant can only decorate a param iff it is also decorated with position
+%my_func = func(%my_param:vec4<f32> [@invariant]):void {
+^^^^^^^^
+
+note: # Disassembly
+%my_func = func(%my_param:vec4<f32> [@invariant]):void {
+ $B1: {
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Function_Param_Struct_InvariantWithPosition) {
+ IOAttributes attr;
+ attr.invariant = true;
+ attr.builtin = BuiltinValue::kPosition;
+
+ auto* str_ty =
+ ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("pos"), ty.vec4<f32>(), attr},
+ });
+
+ auto* f = b.Function("my_func", ty.void_());
+ auto* p = b.FunctionParam("my_param", str_ty);
+ f->SetParams({p});
+
+ b.Append(f->Block(), [&] { b.Return(f); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Function_Param_Struct_InvariantWithoutPosition) {
+ IOAttributes attr;
+ attr.invariant = true;
+
+ auto* str_ty =
+ ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("pos"), ty.vec4<f32>(), attr},
+ });
+
+ auto* f = b.Function("my_func", ty.void_());
+ auto* p = b.FunctionParam("my_param", str_ty);
+ f->SetParams({p});
+
+ b.Append(f->Block(), [&] { b.Return(f); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:5:1 error: invariant can only decorate a param member iff it is also decorated with position
+%my_func = func(%my_param:MyStruct):void {
+^^^^^^^^
+
+note: # Disassembly
+MyStruct = struct @align(16) {
+ pos:vec4<f32> @offset(0), @invariant
+}
+
+%my_func = func(%my_param:MyStruct):void {
+ $B1: {
+ ret
+ }
+}
+)");
+}
+
TEST_F(IR_ValidatorTest, Function_Return_BothLocationAndBuiltin) {
auto* f = b.Function("my_func", ty.f32());
@@ -506,6 +604,98 @@
)");
}
+TEST_F(IR_ValidatorTest, Function_Return_InvariantWithPosition) {
+ IOAttributes attr;
+ attr.builtin = BuiltinValue::kPosition;
+ attr.invariant = true;
+
+ auto* f = b.Function("my_func", ty.vec4<f32>());
+ f->SetReturnAttributes(attr);
+
+ b.Append(f->Block(), [&] { b.Unreachable(); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Function_Return_InvariantWithoutPosition) {
+ IOAttributes attr;
+ attr.invariant = true;
+
+ auto* f = b.Function("my_func", ty.vec4<f32>());
+ f->SetReturnAttributes(attr);
+
+ b.Append(f->Block(), [&] { b.Unreachable(); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:1:1 error: invariant can only decorate a return iff it is also decorated with position
+%my_func = func():vec4<f32> [@invariant] {
+^^^^^^^^
+
+note: # Disassembly
+%my_func = func():vec4<f32> [@invariant] {
+ $B1: {
+ unreachable
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Function_Return_Struct_InvariantWithPosition) {
+ IOAttributes attr;
+ attr.invariant = true;
+ attr.builtin = BuiltinValue::kPosition;
+
+ auto* str_ty =
+ ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("pos"), ty.vec4<f32>(), attr},
+ });
+
+ auto* f = b.Function("my_func", str_ty);
+
+ b.Append(f->Block(), [&] { b.Unreachable(); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Function_Return_Struct_InvariantWithoutPosition) {
+ IOAttributes attr;
+ attr.invariant = true;
+
+ auto* str_ty =
+ ty.Struct(mod.symbols.New("MyStruct"), {
+ {mod.symbols.New("pos"), ty.vec4<f32>(), attr},
+ });
+
+ auto* f = b.Function("my_func", str_ty);
+
+ b.Append(f->Block(), [&] { b.Unreachable(); });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:5:1 error: invariant can only decorate a member iff it is also decorated with position
+%my_func = func():MyStruct {
+^^^^^^^^
+
+note: # Disassembly
+MyStruct = struct @align(16) {
+ pos:vec4<f32> @offset(0), @invariant
+}
+
+%my_func = func():MyStruct {
+ $B1: {
+ unreachable
+ }
+}
+)");
+}
+
TEST_F(IR_ValidatorTest, Function_MissingWorkgroupSize) {
auto* f = b.Function("f", ty.void_(), Function::PipelineStage::kCompute);
b.Append(f->Block(), [&] { b.Return(f); });