spirv-fuzz: Add fuzzer pass to permute function parameters (#3212)

Fixes #3194.
This commit is contained in:
Vasyl Teliman 2020-03-08 16:27:05 +02:00 committed by GitHub
parent da4cd21485
commit 533af49812
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 945 additions and 2 deletions

3
.gitignore vendored
View File

@ -29,3 +29,6 @@ bazel-testlogs
# C-Lion
/.idea/
/cmake-build-*/
# VSCode
/.vscode/*

View File

@ -62,6 +62,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_obfuscate_constants.h
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_permute_function_parameters.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
fuzzer_util.h
@ -104,6 +105,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_merge_blocks.h
transformation_move_block_down.h
transformation_outline_function.h
transformation_permute_function_parameters.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
@ -150,6 +152,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_obfuscate_constants.cpp
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
fuzzer_util.cpp
@ -191,6 +194,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_merge_blocks.cpp
transformation_move_block_down.cpp
transformation_outline_function.cpp
transformation_permute_function_parameters.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp

View File

@ -45,6 +45,7 @@
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@ -244,6 +245,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);

View File

@ -60,6 +60,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
@ -146,6 +147,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
chance_of_outlining_function_ =
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_permuting_parameters_ =
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);

View File

@ -62,6 +62,40 @@ class FuzzerContext {
return result;
}
// Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
// |lo| and |hi| must be valid indices to the |sequence|
template <typename T>
void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
auto& array = *sequence;
if (array.empty()) {
return;
}
assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid");
// i > lo to account for potential infinite loop when lo == 0
for (size_t i = hi; i > lo; --i) {
auto index =
random_generator_->RandomUint32(static_cast<uint32_t>(i - lo + 1));
if (lo + index != i) {
// Introduce std::swap to the scope but don't use it
// directly since there might be a better overload
using std::swap;
swap(array[lo + index], array[i]);
}
}
}
// Ramdomly shuffles a |sequence|
template <typename T>
void Shuffle(std::vector<T>* sequence) const {
if (!sequence->empty()) {
Shuffle(sequence, 0, sequence->size() - 1);
}
}
// Yields an id that is guaranteed not to be used in the module being fuzzed,
// or to have been issued before.
uint32_t GetFreshId();
@ -139,6 +173,9 @@ class FuzzerContext {
uint32_t GetChanceOfOutliningFunction() {
return chance_of_outlining_function_;
}
uint32_t GetChanceOfPermutingParameters() {
return chance_of_permuting_parameters_;
}
uint32_t GetChanceOfReplacingIdWithSynonym() {
return chance_of_replacing_id_with_synonym_;
}
@ -203,6 +240,7 @@ class FuzzerContext {
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_splitting_block_;

View File

@ -22,6 +22,7 @@
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_function.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_matrix.h"
#include "source/fuzz/transformation_add_type_pointer.h"
@ -179,6 +180,26 @@ uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
return result;
}
uint32_t FuzzerPass::FindOrCreateFunctionType(
uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
// FindFunctionType has a sigle argument for OpTypeFunction operands
// so we will have to copy them all in this vector
std::vector<uint32_t> type_ids(argument_id.size() + 1);
type_ids[0] = return_type_id;
std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1);
// Check if type exists
auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(
TransformationAddTypeFunction(result, return_type_id, argument_id));
return result;
}
uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
uint32_t component_count) {
assert(component_count >= 2 && component_count <= 4 &&

View File

@ -112,6 +112,12 @@ class FuzzerPass {
// instruction does not exist, a transformation is applied to add it.
uint32_t FindOrCreate32BitFloatType();
// Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
// instruction. If such an instruction doesn't exist, a transformation
// is applied to create a new one.
uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
const std::vector<uint32_t>& argument_id);
// Returns the id of an OpTypeVector instruction, with |component_type_id|
// (which must already exist) as its base type, and |component_count|
// elements (which must be in the range [2, 4]). If such an instruction does

View File

@ -0,0 +1,81 @@
// 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 <numeric>
#include <vector>
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
namespace spvtools {
namespace fuzz {
FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
default;
void FuzzerPassPermuteFunctionParameters::Apply() {
for (const auto& function : *GetIRContext()->module()) {
uint32_t function_id = function.result_id();
// Skip the function if it is an entry point
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) {
continue;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfPermutingParameters())) {
continue;
}
// Compute permutation for parameters
auto* function_type =
fuzzerutil::GetFunctionType(GetIRContext(), &function);
assert(function_type && "Function type is null");
// Don't take return type into account
uint32_t arg_size = function_type->NumInOperands() - 1;
// Create a vector, fill it with [0, n-1] values and shuffle it
std::vector<uint32_t> permutation(arg_size);
std::iota(permutation.begin(), permutation.end(), 0);
GetFuzzerContext()->Shuffle(&permutation);
// Create a new OpFunctionType instruction with permuted arguments
// if needed
auto result_type_id = function_type->GetSingleWordInOperand(0);
std::vector<uint32_t> argument_ids;
for (auto index : permutation) {
// +1 to take function's return type into account
argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
}
// Apply our transformation
ApplyTransformation(TransformationPermuteFunctionParameters(
function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
permutation));
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,45 @@
// 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_PERMUTE_FUNCTION_PARAMETERS_H_
#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Fuzzer pass that, given a non-entry-point function taking n parameters
// and a permutation of the set [0, n - 1]:
// 1. Introduces a new function type that is the same as the original
// function's type but with the order of arguments permuted
// (only add this if it doesn't already exist)
// 2. Changes the type of the function to this type
// 3. Adjusts all calls to the function so that their arguments are permuted
class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
public:
FuzzerPassPermuteFunctionParameters(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassPermuteFunctionParameters();
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_

View File

@ -395,6 +395,12 @@ uint32_t FindFunctionType(opt::IRContext* ir_context,
return 0;
}
opt::Instruction* GetFunctionType(opt::IRContext* context,
const opt::Function* function) {
uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
return context->get_def_use_mgr()->GetDef(type_id);
}
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
for (auto& function : *ir_context->module()) {
if (function.result_id() == function_id) {
@ -404,6 +410,15 @@ opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
return nullptr;
}
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
for (auto& entry_point : context->module()->entry_points()) {
if (entry_point.GetSingleWordInOperand(1) == function_id) {
return true;
}
}
return false;
}
bool IdIsAvailableAtUse(opt::IRContext* context,
opt::Instruction* use_instruction,
uint32_t use_input_operand_index, uint32_t id) {

View File

@ -152,10 +152,20 @@ bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
uint32_t FindFunctionType(opt::IRContext* ir_context,
const std::vector<uint32_t>& type_ids);
// Returns a type instruction (OpTypeFunction) for |function|.
// Returns |nullptr| if type is not found.
opt::Instruction* GetFunctionType(opt::IRContext* context,
const opt::Function* function);
// Returns the function with result id |function_id|, or |nullptr| if no such
// function exists.
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
// Returns |true| if one of entry points has function id |function_id|.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3218):
// TransformationAddFunctionCall also has this functionality as a static method
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
// Checks whether |id| is available (according to dominance rules) at the use
// point defined by input operand |use_input_operand_index| of
// |use_instruction|.

View File

@ -370,6 +370,7 @@ message Transformation {
TransformationAccessChain access_chain = 39;
TransformationEquationInstruction equation_instruction = 40;
TransformationSwapCommutableOperands swap_commutable_operands = 41;
TransformationPermuteFunctionParameters permute_function_parameters = 42;
// Add additional option using the next available number.
}
}
@ -907,6 +908,36 @@ message TransformationOutlineFunction {
}
message TransformationPermuteFunctionParameters {
// A transformation that, given a non-entry-point function taking n
// parameters and a permutation of the set [0, n-1]:
// - Introduces a new function type that is the same as the original
// function's type but with the order of arguments permuted
// (only if it doesn't already exist)
// - Changes the type of the function to this type
// - Adjusts all calls to the function so that their arguments are permuted
// Function, whose parameters will be permuted
uint32 function_id = 1;
// |new_type_id| is a result id of a valid OpTypeFunction instruction.
// New type is valid if:
// - it has the same number of operands as the old one
// - function's result type is the same as the old one
// - function's arguments are permuted according to |permutation| vector
uint32 new_type_id = 2;
// An array of size |n|, where |n| is a number of arguments to a function
// with |function_id|. For each i: 0 <= permutation[i] < n.
//
// i-th element of this array contains a position for an i-th
// function's argument (i.e. i-th argument will be permutation[i]-th
// after running this transformation)
repeated uint32 permutation = 3;
}
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with

View File

@ -47,6 +47,7 @@
#include "source/fuzz/transformation_merge_blocks.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
@ -144,6 +145,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kOutlineFunction:
return MakeUnique<TransformationOutlineFunction>(
message.outline_function());
case protobufs::Transformation::TransformationCase::
kPermuteFunctionParameters:
return MakeUnique<TransformationPermuteFunctionParameters>(
message.permute_function_parameters());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(

View File

@ -37,7 +37,7 @@ class TransformationAddTypeFunction : public Transformation {
// - |message_.return_type_id| and each element of |message_.argument_type_id|
// must be the ids of non-function types
// - The module must not contain an OpTypeFunction instruction defining a
// function type with the signature provided by teh given return and
// function type with the signature provided by the given return and
// argument types
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;

View File

@ -0,0 +1,184 @@
// 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 <unordered_set>
#include <vector>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
namespace spvtools {
namespace fuzz {
TransformationPermuteFunctionParameters::
TransformationPermuteFunctionParameters(
const spvtools::fuzz::protobufs::
TransformationPermuteFunctionParameters& message)
: message_(message) {}
TransformationPermuteFunctionParameters::
TransformationPermuteFunctionParameters(
uint32_t function_id, uint32_t new_type_id,
const std::vector<uint32_t>& permutation) {
message_.set_function_id(function_id);
message_.set_new_type_id(new_type_id);
for (auto index : permutation) {
message_.add_permutation(index);
}
}
bool TransformationPermuteFunctionParameters::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
// Check that function exists
const auto* function =
fuzzerutil::FindFunction(context, message_.function_id());
if (!function || function->DefInst().opcode() != SpvOpFunction ||
fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) {
return false;
}
// Check that permutation has valid indices
const auto* function_type = fuzzerutil::GetFunctionType(context, function);
assert(function_type && "Function type is null");
const auto& permutation = message_.permutation();
// Don't take return type into account
auto arg_size = function_type->NumInOperands() - 1;
// |permutation| vector should be equal to the number of arguments
if (static_cast<uint32_t>(permutation.size()) != arg_size) {
return false;
}
// Check that all indices are valid
// and unique integers from the [0, n-1] set
std::unordered_set<uint32_t> unique_indices;
for (auto index : permutation) {
// We don't compare |index| with 0 since it's an unsigned integer
if (index >= arg_size) {
return false;
}
unique_indices.insert(index);
}
// Check that permutation doesn't have duplicated values
assert(unique_indices.size() == arg_size && "Permutation has duplicates");
// Check that new function's type is valid:
// - Has the same number of operands
// - Has the same result type as the old one
// - Order of arguments is permuted
auto new_type_id = message_.new_type_id();
const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id);
if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
new_type->NumInOperands() != function_type->NumInOperands()) {
return false;
}
// Check that both instructions have the same result type
if (new_type->GetSingleWordInOperand(0) !=
function_type->GetSingleWordInOperand(0)) {
return false;
}
// Check that new function type has its arguments permuted
for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
// +1 to take return type into account
if (new_type->GetSingleWordInOperand(i + 1) !=
function_type->GetSingleWordInOperand(permutation[i] + 1)) {
return false;
}
}
return true;
}
void TransformationPermuteFunctionParameters::Apply(
opt::IRContext* context, FactManager* /*unused*/) const {
// Retrieve all data from the message
uint32_t function_id = message_.function_id();
uint32_t new_type_id = message_.new_type_id();
const auto& permutation = message_.permutation();
// Find the function that will be transformed
auto* function = fuzzerutil::FindFunction(context, function_id);
assert(function && "Can't find the function");
// Change function's type
function->DefInst().SetInOperand(1, {new_type_id});
// Adjust OpFunctionParameter instructions
// Collect ids and types from OpFunctionParameter instructions
std::vector<uint32_t> param_id, param_type;
function->ForEachParam(
[&param_id, &param_type](const opt::Instruction* param) {
param_id.push_back(param->result_id());
param_type.push_back(param->type_id());
});
// Permute parameters' ids and types
std::vector<uint32_t> permuted_param_id, permuted_param_type;
for (auto index : permutation) {
permuted_param_id.push_back(param_id[index]);
permuted_param_type.push_back(param_type[index]);
}
// Set OpFunctionParameter instructions to point to new parameters
size_t i = 0;
function->ForEachParam(
[&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) {
param->SetResultType(permuted_param_type[i]);
param->SetResultId(permuted_param_id[i]);
++i;
});
// Fix all OpFunctionCall instructions
context->get_def_use_mgr()->ForEachUser(
&function->DefInst(),
[function_id, &permutation](opt::Instruction* call) {
if (call->opcode() != SpvOpFunctionCall ||
call->GetSingleWordInOperand(0) != function_id) {
return;
}
opt::Instruction::OperandList call_operands = {
call->GetInOperand(0) // Function id
};
for (auto index : permutation) {
// Take function id into account
call_operands.push_back(call->GetInOperand(index + 1));
}
call->SetInOperands(std::move(call_operands));
});
// Make sure our changes are analyzed
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_permute_function_parameters() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,61 @@
// 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_PERMUTE_FUNCTION_PARAMETERS_H_
#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationPermuteFunctionParameters : public Transformation {
public:
explicit TransformationPermuteFunctionParameters(
const protobufs::TransformationPermuteFunctionParameters& message);
TransformationPermuteFunctionParameters(
uint32_t function_id, uint32_t new_type_id,
const std::vector<uint32_t>& permutation);
// - |function_id| is a valid non-entry-point OpFunction instruction
// - |new_type_id| is a result id of a valid OpTypeFunction instruction.
// New type is valid if:
// - it has the same number of operands as the old one
// - function's result type is the same as the old one
// - function's arguments are permuted according to |permutation| vector
// - |permutation| is a set of [0..(n - 1)], where n is a number of arguments
// to the function
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// - OpFunction instruction with |result_id == function_id| is changed.
// Its arguments are permuted according to the |permutation| vector
// - Changed function gets a new type specified by |type_id|
// - Calls to the function are adjusted accordingly
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationPermuteFunctionParameters message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_

View File

@ -54,6 +54,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_merge_blocks_test.cpp
transformation_move_block_down_test.cpp
transformation_outline_function_test.cpp
transformation_permute_function_parameters_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp

View File

@ -0,0 +1,430 @@
// 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_permute_function_parameters.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %72 %74
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %12 "g(f1;f1;"
OpName %10 "x"
OpName %11 "y"
OpName %22 "f(f1;i1;vf2;"
OpName %19 "x"
OpName %20 "y"
OpName %21 "z"
OpName %28 "cond(i1;f1;"
OpName %26 "a"
OpName %27 "b"
OpName %53 "param"
OpName %54 "param"
OpName %66 "param"
OpName %67 "param"
OpName %72 "color"
OpName %74 "gl_FragCoord"
OpName %75 "param"
OpName %79 "param"
OpName %85 "param"
OpName %86 "param"
OpName %91 "param"
OpName %92 "param"
OpName %93 "param"
OpName %99 "param"
OpName %100 "param"
OpName %101 "param"
OpDecorate %20 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %47 RelaxedPrecision
OpDecorate %58 RelaxedPrecision
OpDecorate %72 Location 0
OpDecorate %74 BuiltIn FragCoord
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%8 = OpTypeVector %6 4
%9 = OpTypeFunction %8 %7 %7
%14 = OpTypeInt 32 1
%15 = OpTypePointer Function %14
%16 = OpTypeVector %6 2
%17 = OpTypePointer Function %16
%18 = OpTypeFunction %8 %7 %15 %17
%24 = OpTypeBool
%25 = OpTypeFunction %24 %15 %7
%105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
%31 = OpConstant %6 255
%33 = OpConstant %6 0
%34 = OpConstant %6 1
%42 = OpTypeInt 32 0
%43 = OpConstant %42 0
%49 = OpConstant %42 1
%64 = OpConstant %14 4
%65 = OpConstant %6 5
%71 = OpTypePointer Output %8
%72 = OpVariable %71 Output
%73 = OpTypePointer Input %8
%74 = OpVariable %73 Input
%76 = OpTypePointer Input %6
%84 = OpConstant %14 5
%90 = OpConstant %6 3
%98 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%66 = OpVariable %15 Function
%67 = OpVariable %7 Function
%75 = OpVariable %7 Function
%79 = OpVariable %7 Function
%85 = OpVariable %15 Function
%86 = OpVariable %7 Function
%91 = OpVariable %7 Function
%92 = OpVariable %15 Function
%93 = OpVariable %17 Function
%99 = OpVariable %7 Function
%100 = OpVariable %15 Function
%101 = OpVariable %17 Function
OpStore %66 %64
OpStore %67 %65
%68 = OpFunctionCall %24 %28 %66 %67
OpSelectionMerge %70 None
OpBranchConditional %68 %69 %83
%69 = OpLabel
%77 = OpAccessChain %76 %74 %43
%78 = OpLoad %6 %77
OpStore %75 %78
%80 = OpAccessChain %76 %74 %49
%81 = OpLoad %6 %80
OpStore %79 %81
%82 = OpFunctionCall %8 %12 %75 %79
OpStore %72 %82
OpBranch %70
%83 = OpLabel
OpStore %85 %84
OpStore %86 %65
%87 = OpFunctionCall %24 %28 %85 %86
OpSelectionMerge %89 None
OpBranchConditional %87 %88 %97
%88 = OpLabel
OpStore %91 %90
OpStore %92 %64
%94 = OpLoad %8 %74
%95 = OpVectorShuffle %16 %94 %94 0 1
OpStore %93 %95
%96 = OpFunctionCall %8 %22 %91 %92 %93
OpStore %72 %96
OpBranch %89
%97 = OpLabel
OpStore %99 %98
OpStore %100 %84
%102 = OpLoad %8 %74
%103 = OpVectorShuffle %16 %102 %102 0 1
OpStore %101 %103
%104 = OpFunctionCall %8 %22 %99 %100 %101
OpStore %72 %104
OpBranch %89
%89 = OpLabel
OpBranch %70
%70 = OpLabel
OpReturn
OpFunctionEnd
%12 = OpFunction %8 None %9
%10 = OpFunctionParameter %7
%11 = OpFunctionParameter %7
%13 = OpLabel
%30 = OpLoad %6 %10
%32 = OpFDiv %6 %30 %31
%35 = OpLoad %6 %11
%36 = OpFDiv %6 %35 %31
%37 = OpFSub %6 %34 %36
%38 = OpCompositeConstruct %8 %32 %33 %37 %34
OpReturnValue %38
OpFunctionEnd
%22 = OpFunction %8 None %18
%19 = OpFunctionParameter %7
%20 = OpFunctionParameter %15
%21 = OpFunctionParameter %17
%23 = OpLabel
%53 = OpVariable %7 Function
%54 = OpVariable %7 Function
%41 = OpLoad %6 %19
%44 = OpAccessChain %7 %21 %43
%45 = OpLoad %6 %44
%46 = OpFAdd %6 %41 %45
%47 = OpLoad %14 %20
%48 = OpConvertSToF %6 %47
%50 = OpAccessChain %7 %21 %49
%51 = OpLoad %6 %50
%52 = OpFAdd %6 %48 %51
OpStore %53 %46
OpStore %54 %52
%55 = OpFunctionCall %8 %12 %53 %54
OpReturnValue %55
OpFunctionEnd
%28 = OpFunction %24 None %25
%26 = OpFunctionParameter %15
%27 = OpFunctionParameter %7
%29 = OpLabel
%58 = OpLoad %14 %26
%59 = OpConvertSToF %6 %58
%60 = OpLoad %6 %27
%61 = OpFOrdLessThan %24 %59 %60
OpReturnValue %61
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;
// Can't permute main function
ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
context.get(), fact_manager));
// Can't permute invalid instruction
ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
.IsApplicable(context.get(), fact_manager));
// Permutation has too many values
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
.IsApplicable(context.get(), fact_manager));
// Permutation has too few values
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
.IsApplicable(context.get(), fact_manager));
// Permutation has invalid values
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
.IsApplicable(context.get(), fact_manager));
// Type id is not an OpTypeFunction instruction
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
.IsApplicable(context.get(), fact_manager));
// Type id has incorrect number of operands
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
.IsApplicable(context.get(), fact_manager));
// OpTypeFunction has operands out of order
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
.IsApplicable(context.get(), fact_manager));
// Successful transformations
{
// Function has two operands of the same type:
// initial OpTypeFunction should be enough
TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}
{
TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %72 %74
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %12 "g(f1;f1;"
OpName %10 "x"
OpName %11 "y"
OpName %22 "f(f1;i1;vf2;"
OpName %19 "x"
OpName %20 "y"
OpName %21 "z"
OpName %28 "cond(i1;f1;"
OpName %26 "a"
OpName %27 "b"
OpName %53 "param"
OpName %54 "param"
OpName %66 "param"
OpName %67 "param"
OpName %72 "color"
OpName %74 "gl_FragCoord"
OpName %75 "param"
OpName %79 "param"
OpName %85 "param"
OpName %86 "param"
OpName %91 "param"
OpName %92 "param"
OpName %93 "param"
OpName %99 "param"
OpName %100 "param"
OpName %101 "param"
OpDecorate %20 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %47 RelaxedPrecision
OpDecorate %58 RelaxedPrecision
OpDecorate %72 Location 0
OpDecorate %74 BuiltIn FragCoord
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%8 = OpTypeVector %6 4
%9 = OpTypeFunction %8 %7 %7
%14 = OpTypeInt 32 1
%15 = OpTypePointer Function %14
%16 = OpTypeVector %6 2
%17 = OpTypePointer Function %16
%18 = OpTypeFunction %8 %7 %15 %17
%24 = OpTypeBool
%25 = OpTypeFunction %24 %15 %7
%105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
%31 = OpConstant %6 255
%33 = OpConstant %6 0
%34 = OpConstant %6 1
%42 = OpTypeInt 32 0
%43 = OpConstant %42 0
%49 = OpConstant %42 1
%64 = OpConstant %14 4
%65 = OpConstant %6 5
%71 = OpTypePointer Output %8
%72 = OpVariable %71 Output
%73 = OpTypePointer Input %8
%74 = OpVariable %73 Input
%76 = OpTypePointer Input %6
%84 = OpConstant %14 5
%90 = OpConstant %6 3
%98 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%66 = OpVariable %15 Function
%67 = OpVariable %7 Function
%75 = OpVariable %7 Function
%79 = OpVariable %7 Function
%85 = OpVariable %15 Function
%86 = OpVariable %7 Function
%91 = OpVariable %7 Function
%92 = OpVariable %15 Function
%93 = OpVariable %17 Function
%99 = OpVariable %7 Function
%100 = OpVariable %15 Function
%101 = OpVariable %17 Function
OpStore %66 %64
OpStore %67 %65
%68 = OpFunctionCall %24 %28 %67 %66
OpSelectionMerge %70 None
OpBranchConditional %68 %69 %83
%69 = OpLabel
%77 = OpAccessChain %76 %74 %43
%78 = OpLoad %6 %77
OpStore %75 %78
%80 = OpAccessChain %76 %74 %49
%81 = OpLoad %6 %80
OpStore %79 %81
%82 = OpFunctionCall %8 %12 %79 %75
OpStore %72 %82
OpBranch %70
%83 = OpLabel
OpStore %85 %84
OpStore %86 %65
%87 = OpFunctionCall %24 %28 %86 %85
OpSelectionMerge %89 None
OpBranchConditional %87 %88 %97
%88 = OpLabel
OpStore %91 %90
OpStore %92 %64
%94 = OpLoad %8 %74
%95 = OpVectorShuffle %16 %94 %94 0 1
OpStore %93 %95
%96 = OpFunctionCall %8 %22 %91 %92 %93
OpStore %72 %96
OpBranch %89
%97 = OpLabel
OpStore %99 %98
OpStore %100 %84
%102 = OpLoad %8 %74
%103 = OpVectorShuffle %16 %102 %102 0 1
OpStore %101 %103
%104 = OpFunctionCall %8 %22 %99 %100 %101
OpStore %72 %104
OpBranch %89
%89 = OpLabel
OpBranch %70
%70 = OpLabel
OpReturn
OpFunctionEnd
%12 = OpFunction %8 None %9
%11 = OpFunctionParameter %7
%10 = OpFunctionParameter %7
%13 = OpLabel
%30 = OpLoad %6 %10
%32 = OpFDiv %6 %30 %31
%35 = OpLoad %6 %11
%36 = OpFDiv %6 %35 %31
%37 = OpFSub %6 %34 %36
%38 = OpCompositeConstruct %8 %32 %33 %37 %34
OpReturnValue %38
OpFunctionEnd
%22 = OpFunction %8 None %18
%19 = OpFunctionParameter %7
%20 = OpFunctionParameter %15
%21 = OpFunctionParameter %17
%23 = OpLabel
%53 = OpVariable %7 Function
%54 = OpVariable %7 Function
%41 = OpLoad %6 %19
%44 = OpAccessChain %7 %21 %43
%45 = OpLoad %6 %44
%46 = OpFAdd %6 %41 %45
%47 = OpLoad %14 %20
%48 = OpConvertSToF %6 %47
%50 = OpAccessChain %7 %21 %49
%51 = OpLoad %6 %50
%52 = OpFAdd %6 %48 %51
OpStore %53 %46
OpStore %54 %52
%55 = OpFunctionCall %8 %12 %54 %53
OpReturnValue %55
OpFunctionEnd
%28 = OpFunction %24 None %105
%27 = OpFunctionParameter %7
%26 = OpFunctionParameter %15
%29 = OpLabel
%58 = OpLoad %14 %26
%59 = OpConvertSToF %6 %58
%60 = OpLoad %6 %27
%61 = OpFOrdLessThan %24 %59 %60
OpReturnValue %61
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -33,7 +33,8 @@ AUTHORS = ['The Khronos Group Inc.',
'Google LLC',
'Pierre Moreau',
'Samsung Inc',
'André Perez Maselco']
'André Perez Maselco',
'Vasyl Teliman']
CURRENT_YEAR='2020'
YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)'