dawn-cmake/fuzzers/tint_ast_clone_fuzzer.cc
Ben Clayton ed2b97811e ast: Add Module.Clone()
Deep-clones all `Node`s and `Type`s into a new module.

Instead of writing a million standalone tests that'll only ever test the
existing fields of each type, I've opted to write the tests using
wgsl<->ast<->wgsl conversion. This means the tests require the enabling
of TINT_BUILD_WGSL_READER and TINT_BUILD_WGSL_WRITER, but I believe this
is much easier to maintain.

I'm aware there are probably gaps in the tests, and that even full
coverage is likely to rapidly rot, so I've also added
fuzzers/tint_ast_clone_fuzzer.cc - a fuzzer based test that ensures that
all AST modules can be cloned with identical reproduction.

I've run this across 100 cores of a 3990x for 4 hours, fixing the
single issue it detected.

Note: Expressions do not currently clone their `TypeManager` determined
types. This is for two reasons:
(a) This initial CL is mahoosive enough.
(b) I'm uncertain whether we actually want to clone this info, or to
    re-run the `TypeDeterminer` after each AST transform. Maybe it should
    be optional. Time will tell.

Fixed: tint:307
Change-Id: Id90fab06aaa740c805d12b66f3f11d1f452c6805
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33300
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
2020-12-01 18:04:17 +00:00

104 lines
3.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 <iostream>
#include <string>
#include <unordered_set>
#include "src/reader/wgsl/parser_impl.h"
#include "src/writer/wgsl/generator.h"
#define ASSERT_EQ(A, B) \
do { \
decltype(A) assert_a = (A); \
decltype(B) assert_b = (B); \
if (assert_a != assert_b) { \
std::cerr << "ASSERT_EQ(" #A ", " #B ") failed:\n" \
<< #A << " was: " << assert_a << "\n" \
<< #B << " was: " << assert_b << "\n"; \
__builtin_trap(); \
} \
} while (false)
#define ASSERT_TRUE(A) \
do { \
decltype(A) assert_a = (A); \
if (!assert_a) { \
std::cerr << "ASSERT_TRUE(" #A ") failed:\n" \
<< #A << " was: " << assert_a << "\n"; \
__builtin_trap(); \
} \
} while (false)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::string str(reinterpret_cast<const char*>(data), size);
tint::Source::File file("test.wgsl", str);
// Parse the wgsl, create the src module
tint::Context ctx;
tint::reader::wgsl::ParserImpl parser(&ctx, &file);
parser.set_max_errors(1);
if (!parser.Parse()) {
return 0;
}
auto src = parser.module();
// Clone the src module to dst
auto dst = src.Clone();
// Expect the AST printed with to_str() to match
ASSERT_EQ(src.to_str(), dst.to_str());
// Check that none of the AST nodes or type pointers in dst are found in src
std::unordered_set<tint::ast::Node*> src_nodes;
for (auto& src_node : src.nodes()) {
src_nodes.emplace(src_node.get());
}
std::unordered_set<tint::ast::type::Type*> src_types;
for (auto& src_type : src.types()) {
src_types.emplace(src_type.second.get());
}
for (auto& dst_node : dst.nodes()) {
ASSERT_EQ(src_nodes.count(dst_node.get()), 0u);
}
for (auto& dst_type : dst.types()) {
ASSERT_EQ(src_types.count(dst_type.second.get()), 0u);
}
// Regenerate the wgsl for the src module. We use this instead of the original
// source so that reformatting doesn't impact the final wgsl comparision.
// Note that the src module is moved into the generator and this generator has
// a limited scope, so that the src module is released before we attempt to
// print the dst module.
// This guarantee that all the source module 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.
std::string src_wgsl;
{
tint::writer::wgsl::Generator src_gen(std::move(src));
ASSERT_TRUE(src_gen.Generate());
src_wgsl = src_gen.result();
}
// Print the dst module, check it matches the original source
tint::writer::wgsl::Generator dst_gen(std::move(dst));
ASSERT_TRUE(dst_gen.Generate());
auto dst_wgsl = dst_gen.result();
ASSERT_EQ(src_wgsl, dst_wgsl);
return 0;
}