AST fuzzer: wrap unary operator
Add a mutation that wraps an expression in a unary operator. Valid unary operators depend on the type of the expression. Fixes: tint:1111 Change-Id: If5a63c5da7e3c212acbec4e838d6542303e59481 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/62000 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ryan Harrison <rharrison@chromium.org> Commit-Queue: Alastair Donaldson <afdx@google.com>
This commit is contained in:
parent
e47b313d0c
commit
79db32aff2
|
@ -50,10 +50,14 @@ if (build_with_chromium) {
|
|||
"mutation_finders/change_binary_operators.h",
|
||||
"mutation_finders/replace_identifiers.cc",
|
||||
"mutation_finders/replace_identifiers.h",
|
||||
"mutation_finders/wrap_unary_operators.cc",
|
||||
"mutation_finders/wrap_unary_operators.h",
|
||||
"mutations/change_binary_operator.cc",
|
||||
"mutations/change_binary_operator.h",
|
||||
"mutations/replace_identifier.cc",
|
||||
"mutations/replace_identifier.h",
|
||||
"mutations/wrap_unary_operator.cc",
|
||||
"mutations/wrap_unary_operator.h",
|
||||
"mutator.cc",
|
||||
"mutator.h",
|
||||
"node_id_map.cc",
|
||||
|
|
|
@ -42,8 +42,10 @@ set(LIBTINT_AST_FUZZER_SOURCES
|
|||
mutation_finder.h
|
||||
mutation_finders/change_binary_operators.h
|
||||
mutation_finders/replace_identifiers.h
|
||||
mutation_finders/wrap_unary_operators.h
|
||||
mutations/change_binary_operator.h
|
||||
mutations/replace_identifier.h
|
||||
mutations/wrap_unary_operator.h
|
||||
mutator.h
|
||||
node_id_map.h
|
||||
probability_context.h
|
||||
|
@ -59,8 +61,10 @@ set(LIBTINT_AST_FUZZER_SOURCES ${LIBTINT_AST_FUZZER_SOURCES}
|
|||
mutation_finder.cc
|
||||
mutation_finders/change_binary_operators.cc
|
||||
mutation_finders/replace_identifiers.cc
|
||||
mutation_finders/wrap_unary_operators.cc
|
||||
mutations/change_binary_operator.cc
|
||||
mutations/replace_identifier.cc
|
||||
mutations/wrap_unary_operator.cc
|
||||
mutator.cc
|
||||
node_id_map.cc
|
||||
probability_context.cc
|
||||
|
@ -97,7 +101,8 @@ add_tint_ast_fuzzer(tint_ast_wgsl_writer_fuzzer)
|
|||
if (${TINT_BUILD_TESTS})
|
||||
set(TEST_SOURCES
|
||||
mutations/change_binary_operator_test.cc
|
||||
mutations/replace_identifier_test.cc)
|
||||
mutations/replace_identifier_test.cc
|
||||
mutations/wrap_unary_operator_test.cc)
|
||||
|
||||
add_executable(tint_ast_fuzzer_unittests ${TEST_SOURCES})
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
|
@ -34,6 +35,9 @@ std::unique_ptr<Mutation> Mutation::FromMessage(
|
|||
case protobufs::Mutation::kChangeBinaryOperator:
|
||||
return std::make_unique<MutationChangeBinaryOperator>(
|
||||
message.change_binary_operator());
|
||||
case protobufs::Mutation::kWrapUnaryOperator:
|
||||
return std::make_unique<MutationWrapUnaryOperator>(
|
||||
message.wrap_unary_operator());
|
||||
case protobufs::Mutation::MUTATION_NOT_SET:
|
||||
assert(false && "Mutation is not set");
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2021 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 "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/wrap_unary_operators.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/util.h"
|
||||
#include "src/tint/sem/expression.h"
|
||||
#include "src/tint/sem/statement.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
namespace ast_fuzzer {
|
||||
|
||||
MutationList MutationFinderWrapUnaryOperators::FindMutations(
|
||||
const tint::Program& program,
|
||||
NodeIdMap* node_id_map,
|
||||
ProbabilityContext* probability_context) const {
|
||||
MutationList result;
|
||||
|
||||
// Iterate through all ast nodes and for each expression node, try to wrap
|
||||
// the inside a valid unary operator based on the type of the expression.
|
||||
for (const auto* node : program.ASTNodes().Objects()) {
|
||||
const auto* expr_ast_node = tint::As<ast::Expression>(node);
|
||||
|
||||
// Transformation applies only when the node represents a valid expression.
|
||||
if (!expr_ast_node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* expr_sem_node =
|
||||
tint::As<sem::Expression>(program.Sem().Get(expr_ast_node));
|
||||
|
||||
// Transformation applies only when the semantic node for the given
|
||||
// expression is present.
|
||||
if (!expr_sem_node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<ast::UnaryOp> valid_operators =
|
||||
MutationWrapUnaryOperator::GetValidUnaryWrapper(*expr_sem_node);
|
||||
|
||||
// Transformation only applies when there are available unary operators
|
||||
// for the given expression.
|
||||
if (valid_operators.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ast::UnaryOp unary_op_wrapper =
|
||||
valid_operators[probability_context->GetRandomIndex(valid_operators)];
|
||||
|
||||
result.push_back(std::make_unique<MutationWrapUnaryOperator>(
|
||||
node_id_map->GetId(expr_ast_node), node_id_map->TakeFreshId(),
|
||||
unary_op_wrapper));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t MutationFinderWrapUnaryOperators::GetChanceOfApplyingMutation(
|
||||
ProbabilityContext* probability_context) const {
|
||||
return probability_context->GetChanceOfWrappingUnaryOperators();
|
||||
}
|
||||
|
||||
} // namespace ast_fuzzer
|
||||
} // namespace fuzzers
|
||||
} // namespace tint
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
#ifndef SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATION_FINDERS_WRAP_UNARY_OPERATORS_H_
|
||||
#define SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATION_FINDERS_WRAP_UNARY_OPERATORS_H_
|
||||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finder.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
namespace ast_fuzzer {
|
||||
|
||||
/// Looks for opportunities to apply
|
||||
/// `MutationFinderWrapUnaryOperators`.
|
||||
///
|
||||
/// For each expression in the module, try to wrap it within
|
||||
/// a unary operator.
|
||||
class MutationFinderWrapUnaryOperators : public MutationFinder {
|
||||
public:
|
||||
MutationList FindMutations(
|
||||
const tint::Program& program,
|
||||
NodeIdMap* node_id_map,
|
||||
ProbabilityContext* probability_context) const override;
|
||||
uint32_t GetChanceOfApplyingMutation(
|
||||
ProbabilityContext* probability_context) const override;
|
||||
};
|
||||
|
||||
} // namespace ast_fuzzer
|
||||
} // namespace fuzzers
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATION_FINDERS_WRAP_UNARY_OPERATORS_H_
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2021 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 "src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
namespace ast_fuzzer {
|
||||
|
||||
MutationWrapUnaryOperator::MutationWrapUnaryOperator(
|
||||
protobufs::MutationWrapUnaryOperator message)
|
||||
: message_(std::move(message)) {}
|
||||
|
||||
MutationWrapUnaryOperator::MutationWrapUnaryOperator(
|
||||
uint32_t expression_id,
|
||||
uint32_t fresh_id,
|
||||
ast::UnaryOp unary_op_wrapper) {
|
||||
message_.set_expression_id(expression_id);
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_unary_op_wrapper(static_cast<uint32_t>(unary_op_wrapper));
|
||||
}
|
||||
|
||||
bool MutationWrapUnaryOperator::IsApplicable(
|
||||
const tint::Program& program,
|
||||
const NodeIdMap& node_id_map) const {
|
||||
// Check if id that will be assigned is fresh.
|
||||
if (!node_id_map.IdIsFreshAndValid(message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* expression_ast_node =
|
||||
tint::As<ast::Expression>(node_id_map.GetNode(message_.expression_id()));
|
||||
|
||||
if (!expression_ast_node) {
|
||||
// Either the node is not present with the given id or
|
||||
// the node is not a valid expression type.
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* expression_sem_node =
|
||||
tint::As<sem::Expression>(program.Sem().Get(expression_ast_node));
|
||||
|
||||
if (!expression_sem_node) {
|
||||
// Semantic information for the expression ast node is not present
|
||||
// or the semantic node is not a valid expression type node.
|
||||
return false;
|
||||
}
|
||||
|
||||
ast::UnaryOp unary_op_wrapper =
|
||||
static_cast<ast::UnaryOp>(message_.unary_op_wrapper());
|
||||
|
||||
std::vector<ast::UnaryOp> valid_ops =
|
||||
GetValidUnaryWrapper(*expression_sem_node);
|
||||
|
||||
// There is no available unary operator or |unary_op_wrapper| is a
|
||||
// type that is not allowed for the given expression.
|
||||
if (std::find(valid_ops.begin(), valid_ops.end(), unary_op_wrapper) ==
|
||||
valid_ops.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MutationWrapUnaryOperator::Apply(const NodeIdMap& node_id_map,
|
||||
tint::CloneContext* clone_context,
|
||||
NodeIdMap* new_node_id_map) const {
|
||||
auto* expression_node =
|
||||
tint::As<ast::Expression>(node_id_map.GetNode(message_.expression_id()));
|
||||
|
||||
auto* replacement_expression_node =
|
||||
clone_context->dst->create<ast::UnaryOpExpression>(
|
||||
static_cast<ast::UnaryOp>(message_.unary_op_wrapper()),
|
||||
clone_context->Clone(expression_node));
|
||||
|
||||
clone_context->Replace(expression_node, replacement_expression_node);
|
||||
|
||||
new_node_id_map->Add(replacement_expression_node, message_.fresh_id());
|
||||
}
|
||||
|
||||
protobufs::Mutation MutationWrapUnaryOperator::ToMessage() const {
|
||||
protobufs::Mutation mutation;
|
||||
*mutation.mutable_wrap_unary_operator() = message_;
|
||||
return mutation;
|
||||
}
|
||||
|
||||
std::vector<ast::UnaryOp> MutationWrapUnaryOperator::GetValidUnaryWrapper(
|
||||
const sem::Expression& expr) {
|
||||
const auto* expr_type = expr.Type();
|
||||
if (expr_type->is_bool_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kNot};
|
||||
}
|
||||
|
||||
if (expr_type->is_signed_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kNegation, ast::UnaryOp::kComplement};
|
||||
}
|
||||
|
||||
if (expr_type->is_unsigned_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kComplement};
|
||||
}
|
||||
|
||||
if (expr_type->is_float_scalar_or_vector()) {
|
||||
return {ast::UnaryOp::kNegation};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace ast_fuzzer
|
||||
} // namespace fuzzers
|
||||
} // namespace tint
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
#ifndef SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_WRAP_UNARY_OPERATOR_H_
|
||||
#define SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_WRAP_UNARY_OPERATOR_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation.h"
|
||||
|
||||
#include "src/tint/sem/variable.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
namespace ast_fuzzer {
|
||||
|
||||
/// @see MutationWrapUnaryOperator::Apply
|
||||
class MutationWrapUnaryOperator : public Mutation {
|
||||
public:
|
||||
/// @brief Constructs an instance of this mutation from a protobuf message.
|
||||
/// @param message - protobuf message
|
||||
explicit MutationWrapUnaryOperator(
|
||||
protobufs::MutationWrapUnaryOperator message);
|
||||
|
||||
/// @brief Constructor.
|
||||
/// @param expression_id - the id of an expression.
|
||||
/// @param fresh_id - a fresh id for the created expression node with
|
||||
/// unary operator wrapper.
|
||||
/// @param unary_op_wrapper - a `ast::UnaryOp` instance.
|
||||
MutationWrapUnaryOperator(uint32_t expression_id,
|
||||
uint32_t fresh_id,
|
||||
ast::UnaryOp unary_op_wrapper);
|
||||
|
||||
/// @copybrief Mutation::IsApplicable
|
||||
///
|
||||
/// The mutation is applicable iff:
|
||||
/// - `expression_id` must refer to a valid expression that can be wrapped
|
||||
/// with unary operator.
|
||||
/// - `fresh_id` must be fresh.
|
||||
/// - `unary_op_wrapper` is a unary expression that is valid based on the
|
||||
/// type of the given expression.
|
||||
///
|
||||
/// @copydetails Mutation::IsApplicable
|
||||
bool IsApplicable(const tint::Program& program,
|
||||
const NodeIdMap& node_id_map) const override;
|
||||
|
||||
/// @copybrief Mutation::Apply
|
||||
///
|
||||
/// Wrap an expression in a unary operator that is valid based on
|
||||
/// the type of the expression.
|
||||
///
|
||||
/// @copydetails Mutation::Apply
|
||||
void Apply(const NodeIdMap& node_id_map,
|
||||
tint::CloneContext* clone_context,
|
||||
NodeIdMap* new_node_id_map) const override;
|
||||
|
||||
protobufs::Mutation ToMessage() const override;
|
||||
|
||||
/// Return list of unary operator wrappers allowed for the given
|
||||
/// expression.
|
||||
/// @param expr - an `ast::Expression` instance from node id map.
|
||||
/// @return a list of unary operators.
|
||||
static std::vector<ast::UnaryOp> GetValidUnaryWrapper(
|
||||
const sem::Expression& expr);
|
||||
|
||||
private:
|
||||
protobufs::MutationWrapUnaryOperator message_;
|
||||
};
|
||||
|
||||
} // namespace ast_fuzzer
|
||||
} // namespace fuzzers
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_WRAP_UNARY_OPERATOR_H_
|
|
@ -0,0 +1,548 @@
|
|||
// Copyright 2021 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 <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutator.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/node_id_map.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/probability_context.h"
|
||||
#include "src/tint/program_builder.h"
|
||||
#include "src/tint/reader/wgsl/parser.h"
|
||||
#include "src/tint/writer/wgsl/generator.h"
|
||||
|
||||
namespace tint {
|
||||
namespace fuzzers {
|
||||
namespace ast_fuzzer {
|
||||
namespace {
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable1) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
var a = 5;
|
||||
if (a < 5) {
|
||||
a = 6;
|
||||
}
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
auto expression_id = node_id_map.GetId(
|
||||
main_fn_statements[1]->As<ast::IfStatement>()->condition);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNot),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
var a = 5;
|
||||
if (!((a < 5))) {
|
||||
a = 6;
|
||||
}
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable2) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = vec3<bool>(true, false, true);
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNot),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
let a = !(vec3<bool>(true, false, true));
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable3) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
var a : u32;
|
||||
a = 6u;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[1]->As<ast::AssignmentStatement>()->rhs;
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kComplement),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
var a : u32;
|
||||
a = ~(6u);
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable4) {
|
||||
std::string content = R"(
|
||||
fn main() -> vec2<bool> {
|
||||
var a = (vec2<u32> (1u, 2u) == vec2<u32> (1u, 2u));
|
||||
return a;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::BinaryExpression>()
|
||||
->lhs;
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kComplement),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() -> vec2<bool> {
|
||||
var a = (~(vec2<u32>(1u, 2u)) == vec2<u32>(1u, 2u));
|
||||
return a;
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable5) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a : f32 = -(1.0);
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::UnaryOpExpression>()
|
||||
->expr;
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
let a : f32 = -(-(1.0));
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable6) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
var a : vec4<f32> = vec4<f32>(-1.0, -1.0, -1.0, -1.0);
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
var a : vec4<f32> = -(vec4<f32>(-1.0, -1.0, -1.0, -1.0));
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable7) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
var a = 1;
|
||||
for(var i : i32 = 1; i < 5; i = i + 1) {
|
||||
a = a + 1;
|
||||
}
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[1]
|
||||
->As<ast::ForLoopStatement>()
|
||||
->initializer->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
var a = 1;
|
||||
for(var i : i32 = -(1); (i < 5); i = (i + 1)) {
|
||||
a = (a + 1);
|
||||
}
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, Applicable8) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
var a : vec4<i32> = vec4<i32>(1, 0, -1, 0);
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
ASSERT_TRUE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kComplement),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
writer::wgsl::Options options;
|
||||
auto result = writer::wgsl::Generate(&program, options);
|
||||
ASSERT_TRUE(result.success) << result.error;
|
||||
|
||||
std::string expected_shader = R"(fn main() {
|
||||
var a : vec4<i32> = ~(vec4<i32>(1, 0, -1, 0));
|
||||
}
|
||||
)";
|
||||
ASSERT_EQ(expected_shader, result.wgsl);
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable1) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = mat2x3<f32>(vec3<f32>(1.,0.,1.), vec3<f32>(0.,1.,0.));
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
// There is no unary operator that can be applied to matrix type.
|
||||
ASSERT_FALSE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable2) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = 1;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
// Not cannot be applied to integer types.
|
||||
ASSERT_FALSE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNot),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable3) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = vec2<u32>(1u, 2u);
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
// Negation cannot be applied to unsigned integer scalar or vectors.
|
||||
ASSERT_FALSE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable4) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = 1.5;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
// Cannot wrap float types with complement operator.
|
||||
ASSERT_FALSE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(expression_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kComplement),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable5) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = 1.5;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* expr = main_fn_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable->constructor->As<ast::Expression>();
|
||||
|
||||
const auto expression_id = node_id_map.GetId(expr);
|
||||
ASSERT_NE(expression_id, 0);
|
||||
|
||||
// Id for the replacement expression is not fresh.
|
||||
ASSERT_FALSE(
|
||||
MaybeApplyMutation(program,
|
||||
MutationWrapUnaryOperator(expression_id, expression_id,
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
TEST(WrapUnaryOperatorTest, NotApplicable6) {
|
||||
std::string content = R"(
|
||||
fn main() {
|
||||
let a = 1.5;
|
||||
}
|
||||
)";
|
||||
Source::File file("test.wgsl", content);
|
||||
auto program = reader::wgsl::Parse(&file);
|
||||
ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
|
||||
|
||||
NodeIdMap node_id_map(program);
|
||||
|
||||
const auto& main_fn_statements =
|
||||
program.AST().Functions()[0]->body->statements;
|
||||
|
||||
const auto* statement =
|
||||
main_fn_statements[0]->As<ast::VariableDeclStatement>();
|
||||
|
||||
const auto statement_id = node_id_map.GetId(statement);
|
||||
ASSERT_NE(statement_id, 0);
|
||||
|
||||
// The id provided for the expression is not a valid expression type.
|
||||
ASSERT_FALSE(MaybeApplyMutation(
|
||||
program,
|
||||
MutationWrapUnaryOperator(statement_id, node_id_map.TakeFreshId(),
|
||||
ast::UnaryOp::kNegation),
|
||||
node_id_map, &program, &node_id_map, nullptr));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ast_fuzzer
|
||||
} // namespace fuzzers
|
||||
} // namespace tint
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/replace_identifiers.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/wrap_unary_operators.h"
|
||||
#include "src/tint/fuzzers/tint_ast_fuzzer/node_id_map.h"
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -50,6 +50,8 @@ MutationFinderList CreateMutationFinders(
|
|||
enable_all_mutations, probability_context, &result);
|
||||
MaybeAddFinder<MutationFinderReplaceIdentifiers>(
|
||||
enable_all_mutations, probability_context, &result);
|
||||
MaybeAddFinder<MutationFinderWrapUnaryOperators>(
|
||||
enable_all_mutations, probability_context, &result);
|
||||
} while (result.empty());
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ void NodeIdMap::Add(const ast::Node* node, IdType id) {
|
|||
}
|
||||
}
|
||||
|
||||
bool NodeIdMap::IdIsFreshAndValid(IdType id) {
|
||||
bool NodeIdMap::IdIsFreshAndValid(IdType id) const {
|
||||
return id && !id_to_node_.count(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ class NodeIdMap {
|
|||
/// @param id - an id that is used to check in the map.
|
||||
/// @return true the given id is fresh and valid (non-zero).
|
||||
/// @return false otherwise.
|
||||
bool IdIsFreshAndValid(IdType id);
|
||||
bool IdIsFreshAndValid(IdType id) const;
|
||||
|
||||
/// @brief Returns an id that is guaranteed to be unoccupied in this map.
|
||||
///
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace {
|
|||
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfChangingBinaryOperators = {30, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdentifiers = {30, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfWrappingUnaryOperators = {30, 70};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -31,7 +32,9 @@ ProbabilityContext::ProbabilityContext(RandomGenerator* generator)
|
|||
chance_of_changing_binary_operators_(
|
||||
RandomFromRange(kChanceOfChangingBinaryOperators)),
|
||||
chance_of_replacing_identifiers_(
|
||||
RandomFromRange(kChanceOfReplacingIdentifiers)) {
|
||||
RandomFromRange(kChanceOfReplacingIdentifiers)),
|
||||
chance_of_wrapping_unary_operators_(
|
||||
RandomFromRange(kChanceOfWrappingUnaryOperators)) {
|
||||
assert(generator != nullptr && "generator must not be nullptr");
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,11 @@ class ProbabilityContext {
|
|||
return chance_of_replacing_identifiers_;
|
||||
}
|
||||
|
||||
/// @return the probability of wrapping an expression in a unary operator.
|
||||
uint32_t GetChanceOfWrappingUnaryOperators() const {
|
||||
return chance_of_wrapping_unary_operators_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// @param range - a pair of integers `a` and `b` s.t. `a <= b`.
|
||||
/// @return an random number in the range `[a; b]`.
|
||||
|
@ -74,6 +79,7 @@ class ProbabilityContext {
|
|||
|
||||
uint32_t chance_of_changing_binary_operators_;
|
||||
uint32_t chance_of_replacing_identifiers_;
|
||||
uint32_t chance_of_wrapping_unary_operators_;
|
||||
};
|
||||
|
||||
} // namespace ast_fuzzer
|
||||
|
|
|
@ -20,6 +20,7 @@ message Mutation {
|
|||
oneof mutation {
|
||||
MutationReplaceIdentifier replace_identifier = 1;
|
||||
MutationChangeBinaryOperator change_binary_operator = 2;
|
||||
MutationWrapUnaryOperator wrap_unary_operator = 3;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -59,3 +60,17 @@ message MutationChangeBinaryOperator {
|
|||
// A BinaryOp representing the new binary operator.
|
||||
uint32 new_operator = 2;
|
||||
}
|
||||
|
||||
message MutationWrapUnaryOperator {
|
||||
// This transformation wraps an expression with a allowed unary
|
||||
// expression operator.
|
||||
|
||||
// The id of the expression.
|
||||
uint32 expression_id = 1;
|
||||
|
||||
// A fresh id for the created unary expression.
|
||||
uint32 fresh_id = 2;
|
||||
|
||||
// The unary operator to wrap the expression with.
|
||||
uint32 unary_op_wrapper = 3;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue