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/change_binary_operators.h",
|
||||||
"mutation_finders/replace_identifiers.cc",
|
"mutation_finders/replace_identifiers.cc",
|
||||||
"mutation_finders/replace_identifiers.h",
|
"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.cc",
|
||||||
"mutations/change_binary_operator.h",
|
"mutations/change_binary_operator.h",
|
||||||
"mutations/replace_identifier.cc",
|
"mutations/replace_identifier.cc",
|
||||||
"mutations/replace_identifier.h",
|
"mutations/replace_identifier.h",
|
||||||
|
"mutations/wrap_unary_operator.cc",
|
||||||
|
"mutations/wrap_unary_operator.h",
|
||||||
"mutator.cc",
|
"mutator.cc",
|
||||||
"mutator.h",
|
"mutator.h",
|
||||||
"node_id_map.cc",
|
"node_id_map.cc",
|
||||||
|
|
|
@ -42,8 +42,10 @@ set(LIBTINT_AST_FUZZER_SOURCES
|
||||||
mutation_finder.h
|
mutation_finder.h
|
||||||
mutation_finders/change_binary_operators.h
|
mutation_finders/change_binary_operators.h
|
||||||
mutation_finders/replace_identifiers.h
|
mutation_finders/replace_identifiers.h
|
||||||
|
mutation_finders/wrap_unary_operators.h
|
||||||
mutations/change_binary_operator.h
|
mutations/change_binary_operator.h
|
||||||
mutations/replace_identifier.h
|
mutations/replace_identifier.h
|
||||||
|
mutations/wrap_unary_operator.h
|
||||||
mutator.h
|
mutator.h
|
||||||
node_id_map.h
|
node_id_map.h
|
||||||
probability_context.h
|
probability_context.h
|
||||||
|
@ -59,8 +61,10 @@ set(LIBTINT_AST_FUZZER_SOURCES ${LIBTINT_AST_FUZZER_SOURCES}
|
||||||
mutation_finder.cc
|
mutation_finder.cc
|
||||||
mutation_finders/change_binary_operators.cc
|
mutation_finders/change_binary_operators.cc
|
||||||
mutation_finders/replace_identifiers.cc
|
mutation_finders/replace_identifiers.cc
|
||||||
|
mutation_finders/wrap_unary_operators.cc
|
||||||
mutations/change_binary_operator.cc
|
mutations/change_binary_operator.cc
|
||||||
mutations/replace_identifier.cc
|
mutations/replace_identifier.cc
|
||||||
|
mutations/wrap_unary_operator.cc
|
||||||
mutator.cc
|
mutator.cc
|
||||||
node_id_map.cc
|
node_id_map.cc
|
||||||
probability_context.cc
|
probability_context.cc
|
||||||
|
@ -97,7 +101,8 @@ add_tint_ast_fuzzer(tint_ast_wgsl_writer_fuzzer)
|
||||||
if (${TINT_BUILD_TESTS})
|
if (${TINT_BUILD_TESTS})
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
mutations/change_binary_operator_test.cc
|
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})
|
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/change_binary_operator.h"
|
||||||
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier.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 tint {
|
||||||
namespace fuzzers {
|
namespace fuzzers {
|
||||||
|
@ -34,6 +35,9 @@ std::unique_ptr<Mutation> Mutation::FromMessage(
|
||||||
case protobufs::Mutation::kChangeBinaryOperator:
|
case protobufs::Mutation::kChangeBinaryOperator:
|
||||||
return std::make_unique<MutationChangeBinaryOperator>(
|
return std::make_unique<MutationChangeBinaryOperator>(
|
||||||
message.change_binary_operator());
|
message.change_binary_operator());
|
||||||
|
case protobufs::Mutation::kWrapUnaryOperator:
|
||||||
|
return std::make_unique<MutationWrapUnaryOperator>(
|
||||||
|
message.wrap_unary_operator());
|
||||||
case protobufs::Mutation::MUTATION_NOT_SET:
|
case protobufs::Mutation::MUTATION_NOT_SET:
|
||||||
assert(false && "Mutation is not set");
|
assert(false && "Mutation is not set");
|
||||||
break;
|
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/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/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/fuzzers/tint_ast_fuzzer/node_id_map.h"
|
||||||
|
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
@ -50,6 +50,8 @@ MutationFinderList CreateMutationFinders(
|
||||||
enable_all_mutations, probability_context, &result);
|
enable_all_mutations, probability_context, &result);
|
||||||
MaybeAddFinder<MutationFinderReplaceIdentifiers>(
|
MaybeAddFinder<MutationFinderReplaceIdentifiers>(
|
||||||
enable_all_mutations, probability_context, &result);
|
enable_all_mutations, probability_context, &result);
|
||||||
|
MaybeAddFinder<MutationFinderWrapUnaryOperators>(
|
||||||
|
enable_all_mutations, probability_context, &result);
|
||||||
} while (result.empty());
|
} while (result.empty());
|
||||||
return result;
|
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);
|
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.
|
/// @param id - an id that is used to check in the map.
|
||||||
/// @return true the given id is fresh and valid (non-zero).
|
/// @return true the given id is fresh and valid (non-zero).
|
||||||
/// @return false otherwise.
|
/// @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.
|
/// @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> kChanceOfChangingBinaryOperators = {30, 90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdentifiers = {30, 70};
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdentifiers = {30, 70};
|
||||||
|
const std::pair<uint32_t, uint32_t> kChanceOfWrappingUnaryOperators = {30, 70};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -31,7 +32,9 @@ ProbabilityContext::ProbabilityContext(RandomGenerator* generator)
|
||||||
chance_of_changing_binary_operators_(
|
chance_of_changing_binary_operators_(
|
||||||
RandomFromRange(kChanceOfChangingBinaryOperators)),
|
RandomFromRange(kChanceOfChangingBinaryOperators)),
|
||||||
chance_of_replacing_identifiers_(
|
chance_of_replacing_identifiers_(
|
||||||
RandomFromRange(kChanceOfReplacingIdentifiers)) {
|
RandomFromRange(kChanceOfReplacingIdentifiers)),
|
||||||
|
chance_of_wrapping_unary_operators_(
|
||||||
|
RandomFromRange(kChanceOfWrappingUnaryOperators)) {
|
||||||
assert(generator != nullptr && "generator must not be nullptr");
|
assert(generator != nullptr && "generator must not be nullptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,11 @@ class ProbabilityContext {
|
||||||
return chance_of_replacing_identifiers_;
|
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:
|
private:
|
||||||
/// @param range - a pair of integers `a` and `b` s.t. `a <= b`.
|
/// @param range - a pair of integers `a` and `b` s.t. `a <= b`.
|
||||||
/// @return an random number in the range `[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_changing_binary_operators_;
|
||||||
uint32_t chance_of_replacing_identifiers_;
|
uint32_t chance_of_replacing_identifiers_;
|
||||||
|
uint32_t chance_of_wrapping_unary_operators_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ast_fuzzer
|
} // namespace ast_fuzzer
|
||||||
|
|
|
@ -20,6 +20,7 @@ message Mutation {
|
||||||
oneof mutation {
|
oneof mutation {
|
||||||
MutationReplaceIdentifier replace_identifier = 1;
|
MutationReplaceIdentifier replace_identifier = 1;
|
||||||
MutationChangeBinaryOperator change_binary_operator = 2;
|
MutationChangeBinaryOperator change_binary_operator = 2;
|
||||||
|
MutationWrapUnaryOperator wrap_unary_operator = 3;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,3 +60,17 @@ message MutationChangeBinaryOperator {
|
||||||
// A BinaryOp representing the new binary operator.
|
// A BinaryOp representing the new binary operator.
|
||||||
uint32 new_operator = 2;
|
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