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:
parent
03f361ba8e
commit
b3aab09652
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue