[tint][ir] Validate continue value types match continuing block parameters
Change-Id: I774fafe90576a6d3551baff1e69c19f8847e8ec8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188982
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 0f3010f..05c0fb1 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -1318,6 +1318,11 @@
}
}
+ if (auto* cont = loop->Continuing()) {
+ CheckOperandsMatchTarget(c, Continue::kArgsOperandOffset, c->Args().Length(), cont,
+ cont->Params());
+ }
+
first_continues_.Add(loop, c);
}
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index c3e5c25..0c10076 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -2622,7 +2622,7 @@
)");
}
-TEST_F(IR_ValidatorTest, ContinueOutsideOfLoop) {
+TEST_F(IR_ValidatorTest, Continue_OutsideOfLoop) {
auto* f = b.Function("my_func", ty.void_());
b.Append(f->Block(), [&] {
auto* loop = b.Loop();
@@ -2655,7 +2655,7 @@
)");
}
-TEST_F(IR_ValidatorTest, ContinueInLoopInit) {
+TEST_F(IR_ValidatorTest, Continue_InLoopInit) {
auto* f = b.Function("my_func", ty.void_());
b.Append(f->Block(), [&] {
auto* loop = b.Loop();
@@ -2692,7 +2692,7 @@
)");
}
-TEST_F(IR_ValidatorTest, ContinueInLoopBody) {
+TEST_F(IR_ValidatorTest, Continue_InLoopBody) {
auto* f = b.Function("my_func", ty.void_());
b.Append(f->Block(), [&] {
auto* loop = b.Loop();
@@ -2704,7 +2704,7 @@
ASSERT_EQ(res, Success);
}
-TEST_F(IR_ValidatorTest, ContinueInLoopContinuing) {
+TEST_F(IR_ValidatorTest, Continue_InLoopContinuing) {
auto* f = b.Function("my_func", ty.void_());
b.Append(f->Block(), [&] {
auto* loop = b.Loop();
@@ -2741,6 +2741,160 @@
)");
}
+TEST_F(IR_ValidatorTest, Continue_UnexpectedValues) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* loop = b.Loop();
+ b.Append(loop->Body(), [&] { b.Continue(loop, 1_i, 2_f); });
+ b.Append(loop->Continuing(), [&] { b.BreakIf(loop, true); });
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:5:9 error: continue: provides 2 values but 'loop' block $B3 expects 0 values
+ continue 1i, 2.0f # -> $B3
+ ^^^^^^^^^^^^^^^^^
+
+:4:7 note: in block
+ $B2: { # body
+ ^^^
+
+:7:7 note: 'loop' block $B3 declared here
+ $B3: { # continuing
+ ^^^
+
+note: # Disassembly
+%my_func = func():void {
+ $B1: {
+ loop [b: $B2, c: $B3] { # loop_1
+ $B2: { # body
+ continue 1i, 2.0f # -> $B3
+ }
+ $B3: { # continuing
+ break_if true # -> [t: exit_loop loop_1, f: $B2]
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Continue_MissingValues) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* loop = b.Loop();
+ loop->Continuing()->SetParams({b.BlockParam<i32>(), b.BlockParam<i32>()});
+ b.Append(loop->Body(), [&] { b.Continue(loop); });
+ b.Append(loop->Continuing(), [&] { b.BreakIf(loop, true); });
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:5:9 error: continue: provides 0 values but 'loop' block $B3 expects 2 values
+ continue # -> $B3
+ ^^^^^^^^
+
+:4:7 note: in block
+ $B2: { # body
+ ^^^
+
+:7:7 note: 'loop' block $B3 declared here
+ $B3 (%2:i32, %3:i32): { # continuing
+ ^^^^^^^^^^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void {
+ $B1: {
+ loop [b: $B2, c: $B3] { # loop_1
+ $B2: { # body
+ continue # -> $B3
+ }
+ $B3 (%2:i32, %3:i32): { # continuing
+ break_if true # -> [t: exit_loop loop_1, f: $B2]
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Continue_MismatchedTypes) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* loop = b.Loop();
+ loop->Continuing()->SetParams(
+ {b.BlockParam<i32>(), b.BlockParam<f32>(), b.BlockParam<u32>(), b.BlockParam<bool>()});
+ b.Append(loop->Body(), [&] { b.Continue(loop, 1_i, 2_i, 3_f, false); });
+ b.Append(loop->Continuing(), [&] { b.BreakIf(loop, true); });
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(
+ res.Failure().reason.Str(),
+ R"(:5:22 error: continue: operand with type 'i32' does not match 'loop' block $B3 target type 'f32'
+ continue 1i, 2i, 3.0f, false # -> $B3
+ ^^
+
+:4:7 note: in block
+ $B2: { # body
+ ^^^
+
+:7:20 note: %3 declared here
+ $B3 (%2:i32, %3:f32, %4:u32, %5:bool): { # continuing
+ ^^
+
+:5:26 error: continue: operand with type 'f32' does not match 'loop' block $B3 target type 'u32'
+ continue 1i, 2i, 3.0f, false # -> $B3
+ ^^^^
+
+:4:7 note: in block
+ $B2: { # body
+ ^^^
+
+:7:28 note: %4 declared here
+ $B3 (%2:i32, %3:f32, %4:u32, %5:bool): { # continuing
+ ^^
+
+note: # Disassembly
+%my_func = func():void {
+ $B1: {
+ loop [b: $B2, c: $B3] { # loop_1
+ $B2: { # body
+ continue 1i, 2i, 3.0f, false # -> $B3
+ }
+ $B3 (%2:i32, %3:f32, %4:u32, %5:bool): { # continuing
+ break_if true # -> [t: exit_loop loop_1, f: $B2]
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Continue_MatchedTypes) {
+ auto* f = b.Function("my_func", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* loop = b.Loop();
+ loop->Continuing()->SetParams(
+ {b.BlockParam<i32>(), b.BlockParam<f32>(), b.BlockParam<u32>(), b.BlockParam<bool>()});
+ b.Append(loop->Body(), [&] { b.Continue(loop, 1_i, 2_f, 3_u, false); });
+ b.Append(loop->Continuing(), [&] { b.BreakIf(loop, true); });
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
TEST_F(IR_ValidatorTest, NextIterationOutsideOfLoop) {
auto* f = b.Function("my_func", ty.void_());
b.Append(f->Block(), [&] {
diff --git a/src/tint/lang/spirv/writer/loop_test.cc b/src/tint/lang/spirv/writer/loop_test.cc
index ffa15ae..f8ccb38 100644
--- a/src/tint/lang/spirv/writer/loop_test.cc
+++ b/src/tint/lang/spirv/writer/loop_test.cc
@@ -212,7 +212,7 @@
auto* loop = b.Loop();
b.Append(loop->Body(), [&] {
auto* result = b.Equal(ty.bool_(), 1_i, 2_i);
- b.Continue(loop, result);
+ b.Continue(loop);
b.Append(loop->Continuing(), [&] { //
b.BreakIf(loop, result);