Skip to content

Commit

Permalink
[tint][ir][val] Reject discards outside of functions
Browse files Browse the repository at this point in the history
While working on root causing the issue that led to this change I
found some other related issues that should be addressed. They have
been bundled up into this change:

- Run() is modified so that later validation passes don't run if
  CheckStructureSoundness fails, since they assume that it passed.
- ContainingFunction returns nullptr if the input is in the root
  block, which helps prevent ContainingEntryPoint from crashing in a
  very confusing way.

Fixes: 382298307
Fixes: 382291447
Fixes: 382294238
Change-Id: Ibce8c5c52d6df960a54b0438cff7a07fe740c961
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/218174
Commit-Queue: dan sinclair <[email protected]>
Auto-Submit: Ryan Harrison <[email protected]>
Reviewed-by: dan sinclair <[email protected]>
  • Loading branch information
zoddicus authored and Dawn LUCI CQ committed Dec 5, 2024
1 parent e55148f commit cda4e1d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/tint/lang/core/ir/validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,10 @@ class Validator {
/// @param inst the instruction
/// @returns the function
const ir::Function* ContainingFunction(const ir::Instruction* inst) {
if (inst->Block() == mod_.root_block) {
return nullptr;
}

return block_to_function_.GetOrAdd(inst->Block(), [&] { //
return ContainingFunction(inst->Block()->Parent());
});
Expand All @@ -1259,13 +1263,21 @@ class Validator {
/// @param f the function
/// @returns all end points that call the function
Hashset<const ir::Function*, 4> ContainingEndPoints(const ir::Function* f) {
if (!f) {
return {};
}

Hashset<const ir::Function*, 4> result{};
Hashset<const ir::Function*, 4> visited{f};

auto call_sites = user_func_calls_.GetOr(f, Hashset<const ir::UserCall*, 4>()).Vector();
while (!call_sites.IsEmpty()) {
auto call_site = call_sites.Pop();
auto calling_function = ContainingFunction(call_site);
if (!calling_function) {
continue;
}

if (visited.Contains(calling_function)) {
continue;
}
Expand Down Expand Up @@ -1336,8 +1348,13 @@ Disassembler& Validator::Disassemble() {
Result<SuccessType> Validator::Run() {
RunStructuralSoundnessChecks();

CheckForOrphanedInstructions();
CheckForNonFragmentDiscards();
if (!diagnostics_.ContainsErrors()) {
CheckForOrphanedInstructions();
}

if (!diagnostics_.ContainsErrors()) {
CheckForNonFragmentDiscards();
}

if (diagnostics_.ContainsErrors()) {
diagnostics_.AddNote(Source{}) << "# Disassembly\n" << Disassemble().Text();
Expand Down Expand Up @@ -1861,6 +1878,9 @@ void Validator::CheckRootBlock(const Block* blk) {
AddError(inst) << "root block: invalid instruction: " << inst->TypeInfo().name;
}
},
[&](const core::ir::Discard* d) {
AddError(d) << "root block: invalid instruction: " << d->TypeInfo().name;
},
[&](Default) {
// Note, this validation is looser then it could be. There are only certain
// expressions and builtins which can be used in an override. We can tighten this up
Expand Down
23 changes: 23 additions & 0 deletions src/tint/lang/core/ir/validator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3849,6 +3849,29 @@ note: # Disassembly
)");
}

TEST_F(IR_ValidatorTest, Discard_OutsideFunction) {
auto* d = b.Discard();
mod.root_block->Append(d);

auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
R"(:2:3 error: discard: root block: invalid instruction: tint::core::ir::Discard
discard
^^^^^^^
:1:1 note: in block
$B1: { # root
^^^
note: # Disassembly
$B1: { # root
discard
}
)");
}

TEST_F(IR_ValidatorTest, Terminator_HasResult) {
auto* ret_func = b.Function("ret_func", ty.void_());
b.Append(ret_func->Block(), [&] {
Expand Down

0 comments on commit cda4e1d

Please sign in to comment.