dawn-cmake/src/ast/module_clone_test.cc
Ben Clayton 4183051b54 resolver: Add dependency-graph analysis
Performs a module-scope (global) declaration dependency analysis, so
that out-of-order global declarations can be re-ordered into dependency
order for consumption by the resolver.

The WGSL working group are currently debating whether out-of-order
declarations should be included in WebGPU V1, so this implementation
currently errors if module-scope declarations are declared out-of-order,
and the resolver does not currently use this sorted global list.

The analysis does however provide significantly better error diagnostics
when cyclic dependencies are formed, and when globals are declared
out-of-order.

The DependencyGraph also correctly now detects symbol collisions between
functions and types (tint:1308).

With this change, validation is duplicated between the DependencyGraph
and the Resolver. The now-unreachable validation will be removed from
the Resolver with a followup change.

Fixed: tint:1308
Bug: tint:1266
Change-Id: I809c23a069a86cf429f5ec8ef3ad9a98246766ab
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/69381
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
2021-11-22 11:44:57 +00:00

183 lines
4.8 KiB
C++

// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unordered_set>
#include "gtest/gtest.h"
#include "src/reader/wgsl/parser.h"
#include "src/writer/wgsl/generator.h"
namespace tint {
namespace ast {
namespace {
TEST(ModuleCloneTest, Clone) {
#if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
// Shader that exercises the bulk of the AST nodes and types.
// See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
Source::File file("test.wgsl", R"([[block]]
struct S0 {
[[size(4)]]
m0 : u32;
m1 : array<u32>;
};
[[block]] struct S1 {
[[size(4)]]
m0 : u32;
m1 : array<u32, 6>;
};
let c0 : i32 = 10;
let c1 : bool = true;
type t0 = [[stride(16)]] array<vec4<f32>>;
type t1 = array<vec4<f32>>;
var<private> g0 : u32 = 20u;
var<private> g1 : f32 = 123.0;
[[group(0), binding(0)]] var g2 : texture_2d<f32>;
[[group(1), binding(0)]] var g3 : texture_depth_2d;
[[group(2), binding(0)]] var g4 : texture_storage_2d<rg32float, write>;
[[group(3), binding(0)]] var g5 : texture_depth_cube_array;
[[group(4), binding(0)]] var g6 : texture_external;
var<private> g7 : vec3<f32>;
[[group(0), binding(1)]] var<storage, write> g8 : S0;
[[group(1), binding(1)]] var<storage, read> g9 : S0;
[[group(2), binding(1)]] var<storage, read_write> g10 : S0;
fn f0(p0 : bool) -> f32 {
if (p0) {
return 1.0;
}
return 0.0;
}
fn f1(p0 : f32, p1 : i32) -> f32 {
var l0 : i32 = 3;
var l1 : f32 = 8.0;
var l2 : u32 = bitcast<u32>(4);
var l3 : vec2<u32> = vec2<u32>(u32(l0), u32(l1));
var l4 : S1;
var l5 : u32 = l4.m1[5];
let l6 : ptr<private, u32> = &g0;
loop {
l0 = (p1 + 2);
if (((l0 % 4) == 0)) {
continue;
}
continuing {
if (1 == 2) {
l0 = l0 - 1;
} else {
l0 = l0 - 2;
}
}
}
switch(l2) {
case 0u: {
break;
}
case 1u: {
return f0(true);
}
default: {
discard;
}
}
return 1.0;
}
[[stage(fragment)]]
fn main() {
f1(1.0, 2);
}
let declaration_order_check_0 : i32 = 1;
type declaration_order_check_1 = f32;
fn declaration_order_check_2() {}
type declaration_order_check_3 = f32;
let declaration_order_check_4 : i32 = 1;
)");
// Parse the wgsl, create the src program
auto src = reader::wgsl::Parse(&file);
ASSERT_TRUE(src.IsValid()) << diag::Formatter().format(src.Diagnostics());
// Clone the src program to dst
Program dst(src.Clone());
ASSERT_TRUE(dst.IsValid()) << diag::Formatter().format(dst.Diagnostics());
// Expect the printed strings to match
EXPECT_EQ(Program::printer(&src), Program::printer(&dst));
// Check that none of the AST nodes or type pointers in dst are found in src
std::unordered_set<ast::Node*> src_nodes;
for (auto* src_node : src.ASTNodes().Objects()) {
src_nodes.emplace(src_node);
}
std::unordered_set<sem::Type*> src_types;
for (auto* src_type : src.Types()) {
src_types.emplace(src_type);
}
for (auto* dst_node : dst.ASTNodes().Objects()) {
ASSERT_EQ(src_nodes.count(dst_node), 0u);
}
for (auto* dst_type : dst.Types()) {
ASSERT_EQ(src_types.count(dst_type), 0u);
}
// Regenerate the wgsl for the src program. We use this instead of the
// original source so that reformatting doesn't impact the final wgsl
// comparison.
writer::wgsl::Options options;
std::string src_wgsl;
{
auto result = writer::wgsl::Generate(&src, options);
ASSERT_TRUE(result.success) << result.error;
src_wgsl = result.wgsl;
// Move the src program to a temporary that'll be dropped, so that the src
// program is released before we attempt to print the dst program. This
// guarantee that all the source program nodes and types are destructed and
// freed. ASAN should error if there's any remaining references in dst when
// we try to reconstruct the WGSL.
auto tmp = std::move(src);
}
// Print the dst module, check it matches the original source
auto result = writer::wgsl::Generate(&dst, options);
ASSERT_TRUE(result.success);
auto dst_wgsl = result.wgsl;
ASSERT_EQ(src_wgsl, dst_wgsl);
#else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
GTEST_SKIP() << "ModuleCloneTest requires TINT_BUILD_WGSL_READER and "
"TINT_BUILD_WGSL_WRITER to be enabled";
#endif
}
} // namespace
} // namespace ast
} // namespace tint