[ir] Validate block parameters
Check that they are alive and have the correct parent block.
Add source mapping for block parameters to the disassembler, and
remove `EmitValueList()` which is no longer used.
Change-Id: I392506d187ab558cc27df5676ea3f46bc52268c8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/185524
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index d2cd83c..281cb61 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -188,7 +188,14 @@
if (auto* merge = blk->As<MultiInBlock>()) {
if (!merge->Params().IsEmpty()) {
out_ << " (";
- EmitValueList(merge->Params().Slice());
+ for (auto* p : merge->Params()) {
+ if (p != merge->Params().Front()) {
+ out_ << ", ";
+ }
+ SourceMarker psm(this);
+ EmitValue(p);
+ psm.Store(p);
+ }
out_ << ")";
}
}
@@ -797,15 +804,6 @@
);
}
-void Disassembler::EmitValueList(tint::Slice<const Value* const> values) {
- for (size_t i = 0, n = values.Length(); i < n; i++) {
- if (i > 0) {
- out_ << ", ";
- }
- EmitValue(values[i]);
- }
-}
-
void Disassembler::EmitBinary(const Binary* b) {
SourceMarker sm(this);
EmitValueWithType(b);
diff --git a/src/tint/lang/core/ir/disassembler.h b/src/tint/lang/core/ir/disassembler.h
index a2372d4..6b6f7c6 100644
--- a/src/tint/lang/core/ir/disassembler.h
+++ b/src/tint/lang/core/ir/disassembler.h
@@ -32,6 +32,7 @@
#include "src/tint/lang/core/ir/binary.h"
#include "src/tint/lang/core/ir/block.h"
+#include "src/tint/lang/core/ir/block_param.h"
#include "src/tint/lang/core/ir/call.h"
#include "src/tint/lang/core/ir/if.h"
#include "src/tint/lang/core/ir/loop.h"
@@ -104,6 +105,12 @@
/// @returns the source for the block
Source BlockSource(const Block* blk) { return block_to_src_.GetOr(blk, Source{}); }
+ /// @param param the block parameter to retrieve
+ /// @returns the source for the parameter
+ Source BlockParamSource(const BlockParam* param) {
+ return block_param_to_src_.GetOr(param, Source{});
+ }
+
/// @param func the function to retrieve
/// @returns the source for the function
Source FunctionSource(const Function* func) { return function_to_src_.GetOr(func, Source{}); }
@@ -124,6 +131,11 @@
/// @param src the source location
void SetSource(const Block* blk, Source src) { block_to_src_.Add(blk, src); }
+ /// Stores the given @p src location for @p param block parameter
+ /// @param param the block parameter to store
+ /// @param src the source location
+ void SetSource(const BlockParam* param, Source src) { block_param_to_src_.Add(param, src); }
+
/// Stores the given @p src location for @p func function
/// @param func the function to store
/// @param src the source location
@@ -159,6 +171,8 @@
void Store(const Block* blk) { dis_->SetSource(blk, MakeSource()); }
+ void Store(const BlockParam* param) { dis_->SetSource(param, MakeSource()); }
+
void Store(const Function* func) { dis_->SetSource(func, MakeSource()); }
void Store(const FunctionParam* param) { dis_->SetSource(param, MakeSource()); }
@@ -194,7 +208,6 @@
void EmitValueWithType(const Instruction* val);
void EmitValueWithType(const Value* val);
void EmitValue(const Value* val);
- void EmitValueList(tint::Slice<const ir::Value* const> values);
void EmitBinary(const Binary* b);
void EmitUnary(const Unary* b);
void EmitTerminator(const Terminator* b);
@@ -219,6 +232,7 @@
uint32_t current_output_start_pos_ = 0;
Hashmap<const Block*, Source, 8> block_to_src_;
+ Hashmap<const BlockParam*, Source, 8> block_param_to_src_;
Hashmap<const Instruction*, Source, 8> instruction_to_src_;
Hashmap<IndexedValue, Source, 8> operand_to_src_;
Hashmap<IndexedValue, Source, 8> result_to_src_;
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 3634c7b..24d594d 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -163,6 +163,11 @@
/// @returns the diagnostic
diag::Diagnostic& AddError(const Block* blk);
+ /// Adds an error for the @p param and highlights the parameter in the disassembly
+ /// @param param the parameter
+ /// @returns the diagnostic
+ diag::Diagnostic& AddError(const BlockParam* param);
+
/// Adds an error for the @p func and highlights the function in the disassembly
/// @param func the function
/// @returns the diagnostic
@@ -427,6 +432,12 @@
return AddError(src);
}
+diag::Diagnostic& Validator::AddError(const BlockParam* param) {
+ DisassembleIfNeeded();
+ auto src = dis_.BlockParamSource(param);
+ return AddError(src);
+}
+
diag::Diagnostic& Validator::AddError(const Function* func) {
DisassembleIfNeeded();
auto src = dis_.FunctionSource(func);
@@ -547,6 +558,23 @@
void Validator::CheckBlock(const Block* blk) {
TINT_SCOPED_ASSIGNMENT(current_block_, blk);
+ if (auto* mb = blk->As<MultiInBlock>()) {
+ for (auto* param : mb->Params()) {
+ if (!param->Alive()) {
+ AddError(param) << "destroyed parameter found in block parameter list";
+ return;
+ }
+ if (!param->Block()) {
+ AddError(param) << "block parameter has nullptr parent block";
+ return;
+ } else if (param->Block() != mb) {
+ AddError(param) << "block parameter has incorrect parent block";
+ AddNote(param->Block()) << "parent block declared here";
+ return;
+ }
+ }
+ }
+
if (!blk->Terminator()) {
AddError(blk) << "block: does not end in a terminator instruction";
}
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 681a6b2..8742f8a 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -478,6 +478,115 @@
)");
}
+TEST_F(IR_ValidatorTest, Block_DeadParameter) {
+ auto* f = b.Function("my_func", ty.void_());
+
+ auto* p = b.BlockParam("my_param", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* l = b.Loop();
+ l->Body()->SetParams({p});
+ b.Append(l->Body(), [&] { b.ExitLoop(l); });
+ b.Return(f);
+ });
+
+ p->Destroy();
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:4:20 error: destroyed parameter found in block parameter list
+ %b2 = block (%my_param:f32) { # body
+ ^^^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+ %b1 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block (%my_param:f32) { # body
+ exit_loop # loop_1
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Block_ParameterWithNullBlock) {
+ auto* f = b.Function("my_func", ty.void_());
+
+ auto* p = b.BlockParam("my_param", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* l = b.Loop();
+ l->Body()->SetParams({p});
+ b.Append(l->Body(), [&] { b.ExitLoop(l); });
+ b.Return(f);
+ });
+
+ p->SetBlock(nullptr);
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:4:20 error: block parameter has nullptr parent block
+ %b2 = block (%my_param:f32) { # body
+ ^^^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+ %b1 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block (%my_param:f32) { # body
+ exit_loop # loop_1
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Block_ParameterUsedInMultipleBlocks) {
+ auto* f = b.Function("my_func", ty.void_());
+
+ auto* p = b.BlockParam("my_param", ty.f32());
+ b.Append(f->Block(), [&] {
+ auto* l = b.Loop();
+ l->Body()->SetParams({p});
+ b.Append(l->Body(), [&] { b.Continue(l, p); });
+ l->Continuing()->SetParams({p});
+ b.Append(l->Continuing(), [&] { b.NextIteration(l, p); });
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_EQ(res.Failure().reason.Str(),
+ R"(:4:20 error: block parameter has incorrect parent block
+ %b2 = block (%my_param:f32) { # body
+ ^^^^^^^^^^^^^
+
+:7:7 note: parent block declared here
+ %b3 = block (%my_param:f32) { # continuing
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: # Disassembly
+%my_func = func():void -> %b1 {
+ %b1 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block (%my_param:f32) { # body
+ continue %b3 %my_param:f32
+ }
+ %b3 = block (%my_param:f32) { # continuing
+ next_iteration %b2 %my_param:f32
+ }
+ }
+ ret
+ }
+}
+)");
+}
+
TEST_F(IR_ValidatorTest, Access_NegativeIndex) {
auto* f = b.Function("my_func", ty.void_());
auto* obj = b.FunctionParam(ty.vec3<f32>());