spirv-fuzz: Implement FuzzerPassAddParameters (#3399)

Fixes #3384.
This commit is contained in:
Vasyl Teliman 2020-06-23 19:40:44 +03:00 committed by GitHub
parent 71a5b6770d
commit 29ba53f2a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 699 additions and 1 deletions

View File

@ -48,6 +48,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_loads.h fuzzer_pass_add_loads.h
fuzzer_pass_add_local_variables.h fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h fuzzer_pass_add_no_contraction_decorations.h
fuzzer_pass_add_parameters.h
fuzzer_pass_add_stores.h fuzzer_pass_add_stores.h
fuzzer_pass_add_vector_shuffle_instructions.h fuzzer_pass_add_vector_shuffle_instructions.h
fuzzer_pass_adjust_branch_weights.h fuzzer_pass_adjust_branch_weights.h
@ -94,6 +95,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_global_variable.h transformation_add_global_variable.h
transformation_add_local_variable.h transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h transformation_add_no_contraction_decoration.h
transformation_add_parameters.h
transformation_add_spec_constant_op.h transformation_add_spec_constant_op.h
transformation_add_type_array.h transformation_add_type_array.h
transformation_add_type_boolean.h transformation_add_type_boolean.h
@ -154,6 +156,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_loads.cpp fuzzer_pass_add_loads.cpp
fuzzer_pass_add_local_variables.cpp fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp fuzzer_pass_add_no_contraction_decorations.cpp
fuzzer_pass_add_parameters.cpp
fuzzer_pass_add_stores.cpp fuzzer_pass_add_stores.cpp
fuzzer_pass_add_vector_shuffle_instructions.cpp fuzzer_pass_add_vector_shuffle_instructions.cpp
fuzzer_pass_adjust_branch_weights.cpp fuzzer_pass_adjust_branch_weights.cpp
@ -199,6 +202,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_global_variable.cpp transformation_add_global_variable.cpp
transformation_add_local_variable.cpp transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp transformation_add_no_contraction_decoration.cpp
transformation_add_parameters.cpp
transformation_add_spec_constant_op.cpp transformation_add_spec_constant_op.cpp
transformation_add_type_array.cpp transformation_add_type_array.cpp
transformation_add_type_boolean.cpp transformation_add_type_boolean.cpp

View File

@ -32,6 +32,7 @@
#include "source/fuzz/fuzzer_pass_add_loads.h" #include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h" #include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
#include "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_pass_add_stores.h" #include "source/fuzz/fuzzer_pass_add_stores.h"
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" #include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
@ -224,6 +225,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddLocalVariables>( MaybeAddPass<FuzzerPassAddLocalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context, &passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out); transformation_sequence_out);
MaybeAddPass<FuzzerPassAddParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(), MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context, &transformation_context, &fuzzer_context,
transformation_sequence_out); transformation_sequence_out);

View File

@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70}; const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = { const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
5, 70}; 5, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50}; const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70}; const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70}; const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
@ -81,6 +82,10 @@ const uint32_t kDefaultMaxLoopControlPartialCount = 100;
const uint32_t kDefaultMaxLoopControlPeelCount = 100; const uint32_t kDefaultMaxLoopControlPeelCount = 100;
const uint32_t kDefaultMaxLoopLimit = 20; const uint32_t kDefaultMaxLoopLimit = 20;
const uint32_t kDefaultMaxNewArraySizeLimit = 100; const uint32_t kDefaultMaxNewArraySizeLimit = 100;
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
// think whether there is a better limit on the maximum number of parameters.
const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
const uint32_t kDefaultMaxNumberOfNewParameters = 15;
// Default functions for controlling how deep to go during recursive // Default functions for controlling how deep to go during recursive
// generation/transformation. Keep them in alphabetical order. // generation/transformation. Keep them in alphabetical order.
@ -104,6 +109,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount), max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
max_loop_limit_(kDefaultMaxLoopLimit), max_loop_limit_(kDefaultMaxLoopLimit),
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit), max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
go_deeper_in_constant_obfuscation_( go_deeper_in_constant_obfuscation_(
kDefaultGoDeeperInConstantObfuscation) { kDefaultGoDeeperInConstantObfuscation) {
chance_of_adding_access_chain_ = chance_of_adding_access_chain_ =
@ -129,6 +136,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
chance_of_adding_no_contraction_decoration_ = chance_of_adding_no_contraction_decoration_ =
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
chance_of_adding_parameters =
ChooseBetweenMinAndMax(kChanceOfAddingParameters);
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore); chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
chance_of_adding_vector_shuffle_ = chance_of_adding_vector_shuffle_ =
ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle); ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);

View File

@ -135,6 +135,7 @@ class FuzzerContext {
uint32_t GetChanceOfAddingNoContractionDecoration() { uint32_t GetChanceOfAddingNoContractionDecoration() {
return chance_of_adding_no_contraction_decoration_; return chance_of_adding_no_contraction_decoration_;
} }
uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; } uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
uint32_t GetChanceOfAddingVectorShuffle() { uint32_t GetChanceOfAddingVectorShuffle() {
return chance_of_adding_vector_shuffle_; return chance_of_adding_vector_shuffle_;
@ -210,6 +211,9 @@ class FuzzerContext {
uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() { uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
return max_equivalence_class_size_for_data_synonym_fact_closure_; return max_equivalence_class_size_for_data_synonym_fact_closure_;
} }
uint32_t GetMaximumNumberOfFunctionParameters() {
return max_number_of_function_parameters_;
}
std::pair<uint32_t, uint32_t> GetRandomBranchWeights() { std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
std::pair<uint32_t, uint32_t> branch_weights = {0, 0}; std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
@ -245,6 +249,12 @@ class FuzzerContext {
uint32_t GetRandomLoopLimit() { uint32_t GetRandomLoopLimit() {
return random_generator_->RandomUint32(max_loop_limit_); return random_generator_->RandomUint32(max_loop_limit_);
} }
uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
assert(num_of_params < GetMaximumNumberOfFunctionParameters());
return ChooseBetweenMinAndMax(
{1, std::min(max_number_of_new_parameters_,
GetMaximumNumberOfFunctionParameters() - num_of_params)});
}
uint32_t GetRandomSizeForNewArray() { uint32_t GetRandomSizeForNewArray() {
// Ensure that the array size is non-zero. // Ensure that the array size is non-zero.
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
@ -273,6 +283,7 @@ class FuzzerContext {
uint32_t chance_of_adding_local_variable_; uint32_t chance_of_adding_local_variable_;
uint32_t chance_of_adding_matrix_type_; uint32_t chance_of_adding_matrix_type_;
uint32_t chance_of_adding_no_contraction_decoration_; uint32_t chance_of_adding_no_contraction_decoration_;
uint32_t chance_of_adding_parameters;
uint32_t chance_of_adding_store_; uint32_t chance_of_adding_store_;
uint32_t chance_of_adding_vector_shuffle_; uint32_t chance_of_adding_vector_shuffle_;
uint32_t chance_of_adding_vector_type_; uint32_t chance_of_adding_vector_type_;
@ -309,6 +320,8 @@ class FuzzerContext {
uint32_t max_loop_control_peel_count_; uint32_t max_loop_control_peel_count_;
uint32_t max_loop_limit_; uint32_t max_loop_limit_;
uint32_t max_new_array_size_limit_; uint32_t max_new_array_size_limit_;
uint32_t max_number_of_function_parameters_;
uint32_t max_number_of_new_parameters_;
// Functions to determine with what probability to go deeper when generating // Functions to determine with what probability to go deeper when generating
// or mutating constructs recursively. // or mutating constructs recursively.

View File

@ -0,0 +1,143 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_parameters.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddParameters::FuzzerPassAddParameters(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
void FuzzerPassAddParameters::Apply() {
const auto& type_candidates = ComputeTypeCandidates();
if (type_candidates.empty()) {
// The module contains no suitable types to use in new parameters.
return;
}
// Iterate over all functions in the module.
for (const auto& function : *GetIRContext()->module()) {
// Skip all entry-point functions - we don't want to change those.
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
function.result_id())) {
continue;
}
if (GetNumberOfParameters(function) >=
GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
continue;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingParameters())) {
continue;
}
const auto* type_inst =
fuzzerutil::GetFunctionType(GetIRContext(), &function);
assert(type_inst);
// -1 because we don't take return type into account.
auto num_old_parameters = type_inst->NumInOperands() - 1;
auto num_new_parameters =
GetFuzzerContext()->GetRandomNumberOfNewParameters(
GetNumberOfParameters(function));
std::vector<uint32_t> all_types(num_old_parameters);
std::vector<uint32_t> new_types(num_new_parameters);
std::vector<uint32_t> parameter_ids(num_new_parameters);
std::vector<uint32_t> constant_ids(num_new_parameters);
// Get type ids for old parameters.
for (uint32_t i = 0; i < num_old_parameters; ++i) {
// +1 since we don't take return type into account.
all_types[i] = type_inst->GetSingleWordInOperand(i + 1);
}
for (uint32_t i = 0; i < num_new_parameters; ++i) {
// Get type ids for new parameters.
new_types[i] =
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
// Create constants to initialize new parameters from.
constant_ids[i] = FindOrCreateZeroConstant(new_types[i]);
}
// Append new parameters to the old ones.
all_types.insert(all_types.end(), new_types.begin(), new_types.end());
// Generate result ids for new parameters.
for (auto& id : parameter_ids) {
id = GetFuzzerContext()->GetFreshId();
}
auto result_type_id = type_inst->GetSingleWordInOperand(0);
ApplyTransformation(TransformationAddParameters(
function.result_id(),
FindOrCreateFunctionType(result_type_id, all_types),
std::move(new_types), std::move(parameter_ids),
std::move(constant_ids)));
}
}
std::vector<uint32_t> FuzzerPassAddParameters::ComputeTypeCandidates() const {
std::vector<uint32_t> result;
for (const auto* type_inst : GetIRContext()->module()->GetTypes()) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
// the number of types we support here is limited by the number of types
// supported by |FindOrCreateZeroConstant|.
switch (type_inst->opcode()) {
case SpvOpTypeBool:
case SpvOpTypeInt:
case SpvOpTypeFloat:
case SpvOpTypeArray:
case SpvOpTypeMatrix:
case SpvOpTypeVector:
case SpvOpTypeStruct: {
result.push_back(type_inst->result_id());
} break;
default:
// Ignore other types.
break;
}
}
return result;
}
uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
const opt::Function& function) const {
const auto* type = GetIRContext()->get_type_mgr()->GetType(
function.DefInst().GetSingleWordInOperand(1));
assert(type && type->AsFunction());
return static_cast<uint32_t>(type->AsFunction()->param_types().size());
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,51 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
#include <vector>
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Randomly decides for each non-entry-point function in the module whether to
// add new parameters to it. If so, randomly determines the number of parameters
// to add, their type and creates constants used to initialize them.
class FuzzerPassAddParameters : public FuzzerPass {
public:
FuzzerPassAddParameters(opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddParameters() override;
void Apply() override;
private:
// Uses types, defined in the module, to compute a vector of their ids, which
// will be used as type ids of new parameters.
std::vector<uint32_t> ComputeTypeCandidates() const;
// Returns number of parameters of |function|.
uint32_t GetNumberOfParameters(const opt::Function& function) const;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_

View File

@ -34,7 +34,7 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
FuzzerContext* fuzzer_context, FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations); protobufs::TransformationSequence* transformations);
~FuzzerPassPermuteFunctionParameters(); ~FuzzerPassPermuteFunctionParameters() override;
void Apply() override; void Apply() override;
}; };

View File

@ -380,6 +380,7 @@ message Transformation {
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49; TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50; TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
TransformationPermutePhiOperands permute_phi_operands = 51; TransformationPermutePhiOperands permute_phi_operands = 51;
TransformationAddParameters add_parameters = 52;
// Add additional option using the next available number. // Add additional option using the next available number.
} }
} }
@ -623,6 +624,27 @@ message TransformationAddNoContractionDecoration {
} }
message TransformationAddParameters {
// Adds new parameters into the function.
// Result id of the function to add parameters to.
uint32 function_id = 1;
// New type of the function.
uint32 new_type_id = 2;
// Type ids of parameters to add to the function.
repeated uint32 new_parameter_type = 3;
// Result ids for new parameters.
repeated uint32 new_parameter_id = 4;
// Constants to initialize new parameters from.
repeated uint32 constant_id = 5;
}
message TransformationAddSpecConstantOp { message TransformationAddSpecConstantOp {
// Adds OpSpecConstantOp into the module. // Adds OpSpecConstantOp into the module.

View File

@ -30,6 +30,7 @@
#include "source/fuzz/transformation_add_global_variable.h" #include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_local_variable.h" #include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_parameters.h"
#include "source/fuzz/transformation_add_spec_constant_op.h" #include "source/fuzz/transformation_add_spec_constant_op.h"
#include "source/fuzz/transformation_add_type_array.h" #include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h" #include "source/fuzz/transformation_add_type_boolean.h"
@ -114,6 +115,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kAddNoContractionDecoration: kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>( return MakeUnique<TransformationAddNoContractionDecoration>(
message.add_no_contraction_decoration()); message.add_no_contraction_decoration());
case protobufs::Transformation::TransformationCase::kAddParameters:
return MakeUnique<TransformationAddParameters>(message.add_parameters());
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
return MakeUnique<TransformationAddSpecConstantOp>( return MakeUnique<TransformationAddSpecConstantOp>(
message.add_spec_constant_op()); message.add_spec_constant_op());

View File

@ -0,0 +1,201 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 "source/fuzz/transformation_add_parameters.h"
#include <source/spirv_constant.h>
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
TransformationAddParameters::TransformationAddParameters(
const protobufs::TransformationAddParameters& message)
: message_(message) {}
TransformationAddParameters::TransformationAddParameters(
uint32_t function_id, uint32_t new_type_id,
const std::vector<uint32_t>& new_parameter_type,
const std::vector<uint32_t>& new_parameter_id,
const std::vector<uint32_t>& constant_id) {
message_.set_function_id(function_id);
message_.set_new_type_id(new_type_id);
for (auto id : new_parameter_type) {
message_.add_new_parameter_type(id);
}
for (auto id : new_parameter_id) {
message_.add_new_parameter_id(id);
}
for (auto id : constant_id) {
message_.add_constant_id(id);
}
}
bool TransformationAddParameters::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// Check that function exists
const auto* function =
fuzzerutil::FindFunction(ir_context, message_.function_id());
if (!function ||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
return false;
}
// Validate new parameters.
const auto& new_type_ids = message_.new_parameter_type();
const auto& new_parameter_ids = message_.new_parameter_id();
const auto& constant_ids = message_.constant_id();
// All three vectors must have the same size.
if (new_type_ids.size() != new_parameter_ids.size() ||
new_type_ids.size() != constant_ids.size()) {
return false;
}
// Vectors must have at least one component.
if (new_type_ids.empty()) {
return false;
}
// Check that type ids exist in the module and are not OpTypeVoid.
for (auto id : new_type_ids) {
const auto* type = ir_context->get_type_mgr()->GetType(id);
if (!type || type->AsVoid()) {
return false;
}
}
// Check that all parameter ids are fresh.
for (auto id : new_parameter_ids) {
if (!fuzzerutil::IsFreshId(ir_context, id)) {
return false;
}
}
// Check that constants exist and have valid type.
for (int i = 0, n = constant_ids.size(); i < n; ++i) {
const auto* inst = ir_context->get_def_use_mgr()->GetDef(constant_ids[i]);
if (!inst || inst->type_id() != new_type_ids[i]) {
return false;
}
}
// Validate new function type.
const auto* old_type_inst = fuzzerutil::GetFunctionType(ir_context, function);
const auto* new_type_inst =
ir_context->get_def_use_mgr()->GetDef(message_.new_type_id());
// Both types must exist.
assert(old_type_inst && old_type_inst->opcode() == SpvOpTypeFunction);
if (!new_type_inst || new_type_inst->opcode() != SpvOpTypeFunction) {
return false;
}
auto num_old_parameters = old_type_inst->NumInOperands();
auto num_new_parameters = new_type_ids.size();
// New function type has been added to the module which means that it's valid.
// Thus, we don't need to check whether the limit on the number of arguments
// is satisfied.
// New type = old type + new parameters.
if (new_type_inst->NumInOperands() !=
num_old_parameters + num_new_parameters) {
return false;
}
// Check that old parameters and the return type are preserved.
for (uint32_t i = 0; i < num_old_parameters; ++i) {
if (new_type_inst->GetSingleWordInOperand(i) !=
old_type_inst->GetSingleWordInOperand(i)) {
return false;
}
}
// Check that new parameters have been appended.
for (int i = 0; i < num_new_parameters; ++i) {
if (new_type_inst->GetSingleWordInOperand(i + num_old_parameters) !=
new_type_ids[i]) {
return false;
}
}
return true;
}
void TransformationAddParameters::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
// Retrieve all data from the message
auto function_id = message_.function_id();
const auto& new_parameter_type = message_.new_parameter_type();
const auto& new_parameter_id = message_.new_parameter_id();
const auto& constant_id = message_.constant_id();
// Find the function that will be transformed
auto* function = fuzzerutil::FindFunction(ir_context, function_id);
assert(function && "Can't find the function");
// Change function's type
function->DefInst().SetInOperand(1, {message_.new_type_id()});
// Add new parameters to the function.
for (int i = 0, n = new_parameter_id.size(); i < n; ++i) {
function->AddParameter(MakeUnique<opt::Instruction>(
ir_context, SpvOpFunctionParameter, new_parameter_type[i],
new_parameter_id[i], opt::Instruction::OperandList()));
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
}
// Fix all OpFunctionCall instructions.
ir_context->get_def_use_mgr()->ForEachUser(
&function->DefInst(),
[function_id, &constant_id](opt::Instruction* call) {
if (call->opcode() != SpvOpFunctionCall ||
call->GetSingleWordInOperand(0) != function_id) {
return;
}
for (auto id : constant_id) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
// it would be good to mark this usage of |id| as irrelevant, so that
// we can perform some interesting transformations on it later.
call->AddOperand({SPV_OPERAND_TYPE_ID, {id}});
}
});
// Update module's id bound. We can safely dereference the result of
// max_element since |new_parameter_id| is guaranteed to have elements.
fuzzerutil::UpdateModuleIdBound(
ir_context,
*std::max_element(new_parameter_id.begin(), new_parameter_id.end()));
// Make sure our changes are analyzed.
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::Transformation TransformationAddParameters::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_parameters() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,70 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationAddParameters : public Transformation {
public:
explicit TransformationAddParameters(
const protobufs::TransformationAddParameters& message);
TransformationAddParameters(uint32_t function_id, uint32_t new_type_id,
const std::vector<uint32_t>& new_parameter_type,
const std::vector<uint32_t>& new_parameter_id,
const std::vector<uint32_t>& constant_id);
// - |function_id| must be a valid result id of some non-entry-point function
// in the module.
// - |new_type_id| must be a result id of OpTypeFunction instruction.
// - New type of the function must have the same return type. New function
// parameters must be appended to the old ones.
// - |new_parameter_type| contains result ids of some OpType* instructions in
// the module. It may not contain result ids of OpTypeVoid.
// - |new_parameter_id| contains fresh ids.
// - |constant_id| contains result ids used to initialize new parameters. Type
// ids of these instructions must be the same as |new_parameter_type| (i.e.
// |new_parameter_type[i] == GetDef(constant_id[i])->type_id()|).
// - |new_parameter_id|, |new_parameter_type| and |constant_id| should all
// have the same size and may not be empty.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// - Creates new OpFunctionParameter instructions for the function with
// |function_id|.
// - Changes type of the function to |new_type_id|.
// - Adds ids from |constant_id| to every OpFunctionCall instruction that
// calls the function.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddParameters message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_

View File

@ -37,6 +37,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_add_global_variable_test.cpp transformation_add_global_variable_test.cpp
transformation_add_local_variable_test.cpp transformation_add_local_variable_test.cpp
transformation_add_no_contraction_decoration_test.cpp transformation_add_no_contraction_decoration_test.cpp
transformation_add_parameters_test.cpp
transformation_add_type_array_test.cpp transformation_add_type_array_test.cpp
transformation_add_type_boolean_test.cpp transformation_add_type_boolean_test.cpp
transformation_add_type_float_test.cpp transformation_add_type_float_test.cpp

View File

@ -0,0 +1,177 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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 "source/fuzz/transformation_add_parameters.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddParametersTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%7 = OpTypeBool
%11 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%20 = OpTypeFunction %2 %7
%6 = OpTypeFunction %7 %7
%12 = OpTypeFunction %7 %7 %11
%13 = OpTypeFunction %7 %7 %7
%14 = OpTypeFunction %11 %7 %11
%15 = OpTypeFunction %7 %11 %11
%16 = OpTypeFunction %7 %7 %11 %11
%8 = OpConstant %11 23
%17 = OpConstantTrue %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpFunctionCall %7 %9 %17
OpReturn
OpFunctionEnd
%9 = OpFunction %7 None %6
%19 = OpFunctionParameter %7
%10 = OpLabel
OpReturnValue %17
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Can't modify entry point function.
ASSERT_FALSE(TransformationAddParameters(4, 6, {20}, {21}, {17})
.IsApplicable(context.get(), transformation_context));
// There is no function with result id 10.
ASSERT_FALSE(TransformationAddParameters(29, 12, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// There is no OpTypeFunction instruction with result id 21.
ASSERT_FALSE(TransformationAddParameters(9, 21, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Function type with id 6 has fewer parameters than required.
ASSERT_FALSE(TransformationAddParameters(9, 6, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Function type with id 16 has more parameters than required.
ASSERT_FALSE(TransformationAddParameters(9, 16, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// New function type is not OpTypeFunction instruction.
ASSERT_FALSE(TransformationAddParameters(9, 11, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Return type is invalid.
ASSERT_FALSE(TransformationAddParameters(9, 14, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Types of original parameters are invalid.
ASSERT_FALSE(TransformationAddParameters(9, 15, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Types of new parameters are invalid.
ASSERT_FALSE(TransformationAddParameters(9, 13, {11}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// OpTypeVoid can't be the type of function parameter.
ASSERT_FALSE(TransformationAddParameters(9, 12, {2}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
// Vectors, that describe parameters, have different sizes.
ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {21}, {8})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {}, {8})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {})
.IsApplicable(context.get(), transformation_context));
// Vectors cannot be empty.
ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {}, {})
.IsApplicable(context.get(), transformation_context));
// Parameters' ids are not fresh.
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {20}, {8})
.IsApplicable(context.get(), transformation_context));
// Constants for parameters don't exist.
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {21})
.IsApplicable(context.get(), transformation_context));
// Constants for parameters have invalid type.
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {17})
.IsApplicable(context.get(), transformation_context));
// Correct transformation.
TransformationAddParameters correct(9, 12, {11}, {21}, {8});
ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
correct.Apply(context.get(), &transformation_context);
// The module remains valid.
ASSERT_TRUE(IsValid(env, context.get()));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%7 = OpTypeBool
%11 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%20 = OpTypeFunction %2 %7
%6 = OpTypeFunction %7 %7
%12 = OpTypeFunction %7 %7 %11
%13 = OpTypeFunction %7 %7 %7
%14 = OpTypeFunction %11 %7 %11
%15 = OpTypeFunction %7 %11 %11
%16 = OpTypeFunction %7 %7 %11 %11
%8 = OpConstant %11 23
%17 = OpConstantTrue %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%18 = OpFunctionCall %7 %9 %17 %8
OpReturn
OpFunctionEnd
%9 = OpFunction %7 None %12
%19 = OpFunctionParameter %7
%21 = OpFunctionParameter %11
%10 = OpLabel
OpReturnValue %17
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools