AST fuzzer: Change unary expression operator

Adds a mutation that changes the operator used in a unary expression
to some other type-compatible operator.

Fixes: tint:1486

Change-Id: Icf87652fe9ceedcded88d47daece7401a7811873
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/87223
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Alastair Donaldson <afdx@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Alastair Donaldson <afdx@google.com>
This commit is contained in:
Author: Shiyu Liu 2022-05-04 13:07:29 +00:00 committed by Dawn LUCI CQ
parent 03f361ba8e
commit b3aab09652
13 changed files with 635 additions and 10 deletions

View File

@ -52,12 +52,16 @@ if (build_with_chromium) {
"mutation_finder.h",
"mutation_finders/change_binary_operators.cc",
"mutation_finders/change_binary_operators.h",
"mutation_finders/change_unary_operators.cc",
"mutation_finders/change_unary_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/change_unary_operator.cc",
"mutations/change_unary_operator.h",
"mutations/replace_identifier.cc",
"mutations/replace_identifier.h",
"mutations/wrap_unary_operator.cc",

View File

@ -42,9 +42,11 @@ set(LIBTINT_AST_FUZZER_SOURCES
mutation.h
mutation_finder.h
mutation_finders/change_binary_operators.h
mutation_finders/change_unary_operators.h
mutation_finders/replace_identifiers.h
mutation_finders/wrap_unary_operators.h
mutations/change_binary_operator.h
mutations/change_unary_operator.h
mutations/replace_identifier.h
mutations/wrap_unary_operator.h
mutator.h
@ -62,9 +64,11 @@ set(LIBTINT_AST_FUZZER_SOURCES ${LIBTINT_AST_FUZZER_SOURCES}
mutation.cc
mutation_finder.cc
mutation_finders/change_binary_operators.cc
mutation_finders/change_unary_operators.cc
mutation_finders/replace_identifiers.cc
mutation_finders/wrap_unary_operators.cc
mutations/change_binary_operator.cc
mutations/change_unary_operator.cc
mutations/replace_identifier.cc
mutations/wrap_unary_operator.cc
mutator.cc
@ -104,6 +108,7 @@ if (${TINT_BUILD_TESTS})
set(TEST_SOURCES
expression_size_test.cc
mutations/change_binary_operator_test.cc
mutations/change_unary_operator_test.cc
mutations/replace_identifier_test.cc
mutations/wrap_unary_operator_test.cc)

View File

@ -17,6 +17,7 @@
#include <cassert>
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.h"
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_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"
@ -26,6 +27,8 @@ Mutation::~Mutation() = default;
std::unique_ptr<Mutation> Mutation::FromMessage(const protobufs::Mutation& message) {
switch (message.mutation_case()) {
case protobufs::Mutation::kChangeUnaryOperator:
return std::make_unique<MutationChangeUnaryOperator>(message.change_unary_operator());
case protobufs::Mutation::kReplaceIdentifier:
return std::make_unique<MutationReplaceIdentifier>(message.replace_identifier());
case protobufs::Mutation::kChangeBinaryOperator:

View File

@ -0,0 +1,65 @@
// 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/change_unary_operators.h"
#include <memory>
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h"
#include "src/tint/fuzzers/tint_ast_fuzzer/util.h"
#include "src/tint/sem/reference.h"
namespace tint::fuzzers::ast_fuzzer {
MutationList MutationFinderChangeUnaryOperators::FindMutations(
const tint::Program& program,
NodeIdMap* node_id_map,
ProbabilityContext* /*unused*/) const {
MutationList result;
// Iterate through all AST nodes and for each valid unary op expression node,
// try to replace the node's unary operator.
for (const auto* node : program.ASTNodes().Objects()) {
const auto* unary_expr = tint::As<ast::UnaryOpExpression>(node);
// Transformation applies only when the node represents a valid unary
// expression.
if (!unary_expr) {
continue;
}
// Get the type of the unary expression.
const auto* type = program.Sem().Get(unary_expr)->Type();
const auto* basic_type =
type->Is<sem::Reference>() ? type->As<sem::Reference>()->StoreType() : type;
// Only signed integer or vector of signed integer can be mutated.
if (!basic_type->is_signed_scalar_or_vector()) {
continue;
}
result.push_back(std::make_unique<MutationChangeUnaryOperator>(
node_id_map->GetId(unary_expr),
MutationChangeUnaryOperator::ToggleOperator(unary_expr->op)));
}
return result;
}
uint32_t MutationFinderChangeUnaryOperators::GetChanceOfApplyingMutation(
ProbabilityContext* probability_context) const {
return probability_context->GetChanceOfChangingUnaryOperators();
}
} // namespace tint::fuzzers::ast_fuzzer

View File

@ -0,0 +1,37 @@
// 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_CHANGE_UNARY_OPERATORS_H_
#define SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATION_FINDERS_CHANGE_UNARY_OPERATORS_H_
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finder.h"
namespace tint::fuzzers::ast_fuzzer {
/// Looks for opportunities to apply
/// `MutationFinderChangeUnaryOperators`.
///
/// Concretely, for each unary expression in the module, try to change its
/// operator with a permitted one.
class MutationFinderChangeUnaryOperators : 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 tint::fuzzers::ast_fuzzer
#endif // SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATION_FINDERS_CHANGE_UNARY_OPERATORS_H_

View File

@ -0,0 +1,107 @@
// 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/change_unary_operator.h"
#include <utility>
#include "src/tint/fuzzers/tint_ast_fuzzer/util.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/reference.h"
namespace tint::fuzzers::ast_fuzzer {
MutationChangeUnaryOperator::MutationChangeUnaryOperator(
protobufs::MutationChangeUnaryOperator message)
: message_(std::move(message)) {}
MutationChangeUnaryOperator::MutationChangeUnaryOperator(uint32_t unary_expr_id,
ast::UnaryOp new_operator) {
message_.set_unary_expr_id(unary_expr_id);
message_.set_new_operator(static_cast<uint32_t>(new_operator));
}
bool MutationChangeUnaryOperator::IsApplicable(const tint::Program& program,
const NodeIdMap& node_id_map) const {
const auto* unary_expr_node =
tint::As<ast::UnaryOpExpression>(node_id_map.GetNode(message_.unary_expr_id()));
if (!unary_expr_node) {
// Either the id does not exist, or does not correspond to a unary
// expression.
return false;
}
auto new_unary_operator = static_cast<ast::UnaryOp>(message_.new_operator());
// Get the type of the unary expression.
const auto* type = program.Sem().Get(unary_expr_node)->Type();
const auto* basic_type =
type->Is<sem::Reference>() ? type->As<sem::Reference>()->StoreType() : type;
// Only signed integer or vector of signed integer has more than 1
// unary operators to change between.
if (!basic_type->is_signed_scalar_or_vector()) {
return false;
}
// The new unary operator must not be the same as the original one.
if (new_unary_operator != ToggleOperator(unary_expr_node->op)) {
return false;
}
return true;
}
void MutationChangeUnaryOperator::Apply(const NodeIdMap& node_id_map,
tint::CloneContext* clone_context,
NodeIdMap* new_node_id_map) const {
const auto* unary_expr_node =
tint::As<ast::UnaryOpExpression>(node_id_map.GetNode(message_.unary_expr_id()));
const ast::UnaryOpExpression* cloned_replacement;
switch (static_cast<ast::UnaryOp>(message_.new_operator())) {
case ast::UnaryOp::kComplement:
cloned_replacement =
clone_context->dst->Complement(clone_context->Clone(unary_expr_node->expr));
break;
case ast::UnaryOp::kNegation:
cloned_replacement =
clone_context->dst->Negation(clone_context->Clone(unary_expr_node->expr));
break;
default:
cloned_replacement = nullptr;
assert(false && "Unreachable");
}
// Set things up so that the original unary expression will be replaced with
// its clone, and update the id mapping.
clone_context->Replace(unary_expr_node, cloned_replacement);
new_node_id_map->Add(cloned_replacement, message_.unary_expr_id());
}
protobufs::Mutation MutationChangeUnaryOperator::ToMessage() const {
protobufs::Mutation mutation;
*mutation.mutable_change_unary_operator() = message_;
return mutation;
}
ast::UnaryOp MutationChangeUnaryOperator::ToggleOperator(const ast::UnaryOp& original_op) {
if (original_op == ast::UnaryOp::kComplement) {
return ast::UnaryOp::kNegation;
}
assert(original_op == ast::UnaryOp::kNegation && "Unexpected operator.");
return ast::UnaryOp::kComplement;
}
} // namespace tint::fuzzers::ast_fuzzer

View File

@ -0,0 +1,72 @@
// 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_CHANGE_UNARY_OPERATOR_H_
#define SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_CHANGE_UNARY_OPERATOR_H_
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation.h"
#include "src/tint/sem/variable.h"
namespace tint::fuzzers::ast_fuzzer {
/// @see MutationChangeUnaryOperator::Apply
class MutationChangeUnaryOperator : public Mutation {
public:
/// @brief Constructs an instance of this mutation from a protobuf message.
/// @param message - protobuf message
explicit MutationChangeUnaryOperator(protobufs::MutationChangeUnaryOperator message);
/// @brief Constructor.
/// @param unary_expr_id - the id of the `ast::UnaryOpExpression` instance
/// to change its operator.
/// @param new_operator - A new unary operator for the unary expression
/// specified by `expression_id`.
MutationChangeUnaryOperator(uint32_t unary_expr_id, ast::UnaryOp new_operator);
/// @copybrief Mutation::IsApplicable
///
/// The mutation is applicable if and only if:
/// - `expression_id` is an id of an `ast::UnaryOpExpression`, that references
/// a valid unary expression.
/// - `new_unary_op` is a valid unary operator of type `ast::UnaryOp`
/// to the target expression.
///
/// @copydetails Mutation::IsApplicable
bool IsApplicable(const tint::Program& program, const NodeIdMap& node_id_map) const override;
/// @copybrief Mutation::Apply
///
/// Replaces the operator of an unary op expression with `expression_id`
/// with a new unary operator specified by `new_unary_op'. The modified
/// expression preserves the same type as the original 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;
/// Toggles between the complement and negate unary operators.
/// @param original_op - a complement or negation unary operator.
/// @return the other operator.
static ast::UnaryOp ToggleOperator(const ast::UnaryOp& original_op);
private:
protobufs::MutationChangeUnaryOperator message_;
};
} // namespace tint::fuzzers::ast_fuzzer
#endif // SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_CHANGE_UNARY_OPERATOR_H_

View File

@ -0,0 +1,299 @@
// 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/ast/call_statement.h"
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_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::fuzzers::ast_fuzzer {
namespace {
TEST(ChangeUnaryOperatorTest, Operator_Not_Applicable) {
std::string content = R"(
fn main() {
let a : f32 = 1.1;
let b = vec2<i32>(1, -1);
let c : u32 = 0u;
let d : vec3<bool> = vec3<bool> (false, false, true);
var neg_a = -a;
var not_b = ~b;
var not_c = ~c;
var neg_d = !d;
}
)";
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;
// Get variable from statements.
const auto* neg_a_var = main_fn_statements[4]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(neg_a_var, nullptr);
const auto* not_b_var = main_fn_statements[5]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(not_b_var, nullptr);
const auto* not_c_var = main_fn_statements[6]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(not_c_var, nullptr);
const auto* neg_d_var = main_fn_statements[7]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(neg_d_var, nullptr);
// Get the expression from variable declaration.
const auto* neg_a_expr = neg_a_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(neg_a_expr, nullptr);
const auto* not_b_expr = not_b_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(not_b_expr, nullptr);
const auto* not_c_expr = not_c_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(not_c_expr, nullptr);
const auto* neg_d_expr = neg_d_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(neg_d_expr, nullptr);
// The following mutations are not applicable.
auto neg_a_id = node_id_map.GetId(neg_a_expr);
// Only negation is allowed for float type. Cannot change
// the operator of float types to any other.
ASSERT_FALSE(MaybeApplyMutation(
program, MutationChangeUnaryOperator(neg_a_id, ast::UnaryOp::kComplement), node_id_map,
&program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_a_id, ast::UnaryOp::kNot),
node_id_map, &program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_a_id, ast::UnaryOp::kNegation),
node_id_map, &program, &node_id_map, nullptr));
auto not_b_id = node_id_map.GetId(not_b_expr);
// Only complement and negation is allowed for signed integer type.
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(not_b_id, ast::UnaryOp::kNot),
node_id_map, &program, &node_id_map, nullptr));
// Cannot change to the same unary operator.
ASSERT_FALSE(MaybeApplyMutation(
program, MutationChangeUnaryOperator(not_b_id, ast::UnaryOp::kComplement), node_id_map,
&program, &node_id_map, nullptr));
auto not_c_id = node_id_map.GetId(not_c_expr);
// Only complement is allowed for unsigned integer.Cannot change
// // the operator of float types to any other.
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(not_c_id, ast::UnaryOp::kNot),
node_id_map, &program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(not_c_id, ast::UnaryOp::kNegation),
node_id_map, &program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(
program, MutationChangeUnaryOperator(not_c_id, ast::UnaryOp::kComplement), node_id_map,
&program, &node_id_map, nullptr));
auto neg_d_id = node_id_map.GetId(neg_d_expr);
// Only logical negation (not) is allowed for bool type. Cannot change
// the operator of float types to any other.
ASSERT_FALSE(MaybeApplyMutation(
program, MutationChangeUnaryOperator(neg_d_id, ast::UnaryOp::kComplement), node_id_map,
&program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_d_id, ast::UnaryOp::kNegation),
node_id_map, &program, &node_id_map, nullptr));
ASSERT_FALSE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_d_id, ast::UnaryOp::kNot),
node_id_map, &program, &node_id_map, nullptr));
}
TEST(ChangeUnaryOperatorTest, Signed_Integer_Types_Applicable1) {
std::string content = R"(
fn main() {
let a : i32 = 5;
let comp_a = ~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;
// Get variable from statements.
const auto* comp_a_var = main_fn_statements[1]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(comp_a_var, nullptr);
// Get the expression from variable declaration.
const auto* comp_a_expr = comp_a_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(comp_a_expr, nullptr);
// Assert mutation to be applicable and apply mutation.
auto comp_a_id = node_id_map.GetId(comp_a_expr);
ASSERT_TRUE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(comp_a_id, 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 : i32 = 5;
let comp_a = -(a);
}
)";
ASSERT_EQ(expected_shader, result.wgsl);
}
TEST(ChangeUnaryOperatorTest, Signed_Integer_Types_Applicable2) {
std::string content = R"(
fn main() {
let b : vec3<i32> = vec3<i32>(1, 3, -1);
var comp_b : vec3<i32> = ~b;
})";
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;
// Get variable from statements.
const auto* comp_b_var = main_fn_statements[1]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(comp_b_var, nullptr);
// Get the expression from variable declaration.
const auto* comp_b_expr = comp_b_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(comp_b_expr, nullptr);
// Assert mutation to be applicable and apply mutation.
auto comp_b_id = node_id_map.GetId(comp_b_expr);
ASSERT_TRUE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(comp_b_id, 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 b : vec3<i32> = vec3<i32>(1, 3, -1);
var comp_b : vec3<i32> = -(b);
}
)";
ASSERT_EQ(expected_shader, result.wgsl);
}
TEST(ChangeUnaryOperatorTest, Signed_Integer_Types_Applicable3) {
std::string content = R"(
fn main() {
var a = -5;
var neg_a = -(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;
// Get variable from statements.
const auto* neg_a_var = main_fn_statements[1]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(neg_a_var, nullptr);
// Get the expression from variable declaration.
const auto* neg_a_expr = neg_a_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(neg_a_expr, nullptr);
// Assert mutation to be applicable and apply mutation.
auto neg_a_id = node_id_map.GetId(neg_a_expr);
ASSERT_TRUE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_a_id, 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 = -5;
var neg_a = ~(a);
}
)";
ASSERT_EQ(expected_shader, result.wgsl);
}
TEST(ChangeUnaryOperatorTest, Signed_Integer_Types_Applicable4) {
std::string content = R"(
fn main() {
var b : vec3<i32> = vec3<i32>(1, 3, -1);
let neg_b : vec3<i32> = -b;
}
)";
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;
// Get variable from statements.
const auto* neg_b_var = main_fn_statements[1]->As<ast::VariableDeclStatement>()->variable;
ASSERT_NE(neg_b_var, nullptr);
// Get the expression from variable declaration.
const auto* neg_b_expr = neg_b_var->constructor->As<ast::UnaryOpExpression>();
ASSERT_NE(neg_b_expr, nullptr);
// Assert mutation to be applicable and apply mutation.
auto neg_b_id = node_id_map.GetId(neg_b_expr);
ASSERT_TRUE(MaybeApplyMutation(program,
MutationChangeUnaryOperator(neg_b_id, 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 b : vec3<i32> = vec3<i32>(1, 3, -1);
let neg_b : vec3<i32> = ~(b);
}
)";
ASSERT_EQ(expected_shader, result.wgsl);
}
} // namespace
} // namespace tint::fuzzers::ast_fuzzer

View File

@ -21,6 +21,7 @@
#include <vector>
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_binary_operators.h"
#include "src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_unary_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"
@ -45,6 +46,8 @@ MutationFinderList CreateMutationFinders(ProbabilityContext* probability_context
do {
MaybeAddFinder<MutationFinderChangeBinaryOperators>(enable_all_mutations,
probability_context, &result);
MaybeAddFinder<MutationFinderChangeUnaryOperators>(enable_all_mutations,
probability_context, &result);
MaybeAddFinder<MutationFinderReplaceIdentifiers>(enable_all_mutations, probability_context,
&result);
MaybeAddFinder<MutationFinderWrapUnaryOperators>(enable_all_mutations, probability_context,

View File

@ -20,6 +20,7 @@ namespace tint::fuzzers::ast_fuzzer {
namespace {
const std::pair<uint32_t, uint32_t> kChanceOfChangingBinaryOperators = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfChangingUnaryOperators = {30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdentifiers = {30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfWrappingUnaryOperators = {30, 70};
@ -28,6 +29,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfWrappingUnaryOperators = {30, 70};
ProbabilityContext::ProbabilityContext(RandomGenerator* generator)
: generator_(generator),
chance_of_changing_binary_operators_(RandomFromRange(kChanceOfChangingBinaryOperators)),
chance_of_changing_unary_operators_(RandomFromRange(kChanceOfChangingUnaryOperators)),
chance_of_replacing_identifiers_(RandomFromRange(kChanceOfReplacingIdentifiers)),
chance_of_wrapping_unary_operators_(RandomFromRange(kChanceOfWrappingUnaryOperators)) {
assert(generator != nullptr && "generator must not be nullptr");

View File

@ -56,6 +56,11 @@ class ProbabilityContext {
return chance_of_changing_binary_operators_;
}
/// @return the probability of changing operator for an unary expression.
uint32_t GetChanceOfChangingUnaryOperators() const {
return chance_of_changing_unary_operators_;
}
/// @return the probability of replacing some identifier with some other one.
uint32_t GetChanceOfReplacingIdentifiers() const { return chance_of_replacing_identifiers_; }
@ -72,6 +77,7 @@ class ProbabilityContext {
RandomGenerator* generator_;
uint32_t chance_of_changing_binary_operators_;
uint32_t chance_of_changing_unary_operators_;
uint32_t chance_of_replacing_identifiers_;
uint32_t chance_of_wrapping_unary_operators_;
};

View File

@ -21,6 +21,7 @@ message Mutation {
MutationReplaceIdentifier replace_identifier = 1;
MutationChangeBinaryOperator change_binary_operator = 2;
MutationWrapUnaryOperator wrap_unary_operator = 3;
MutationChangeUnaryOperator change_unary_operator = 4;
};
}
@ -41,15 +42,7 @@ message MutatorState {
MutationSequence mutation_sequence = 2;
}
message MutationReplaceIdentifier {
// This transformation replaces a use of one variable with another.
// The id of the use of a variable in the AST.
uint32 use_id = 1;
// The id of a definition of a variable to replace the use with.
uint32 replacement_id = 2;
}
// Keep mutation messages in alphabetical order.
message MutationChangeBinaryOperator {
// This transformation replaces one binary operator with another.
@ -61,6 +54,26 @@ message MutationChangeBinaryOperator {
uint32 new_operator = 2;
}
message MutationChangeUnaryOperator {
// This transformation replaces one unary operator with another.
// The id of a unary expression in the AST.
uint32 unary_expr_id = 1;
// A UnaryOp representing the new unary operator.
uint32 new_operator = 2;
}
message MutationReplaceIdentifier {
// This transformation replaces a use of one variable with another.
// The id of the use of a variable in the AST.
uint32 use_id = 1;
// The id of a definition of a variable to replace the use with.
uint32 replacement_id = 2;
}
message MutationWrapUnaryOperator {
// This transformation wraps an expression with a allowed unary
// expression operator.

View File

@ -1538,6 +1538,15 @@ class ProgramBuilder {
Expr(std::forward<EXPR>(expr)));
}
/// @param expr the expression to perform a unary negation on
/// @return an ast::UnaryOpExpression that is the unary negation of the
/// input expression
template <typename EXPR>
const ast::UnaryOpExpression* Negation(EXPR&& expr) {
return create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation,
Expr(std::forward<EXPR>(expr)));
}
/// @param source the source information
/// @param func the function name
/// @param args the function call arguments