mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 13:20:05 +00:00
parent
e4aebf99fa
commit
fe9e5db890
@ -75,6 +75,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
fuzzer_pass_push_ids_through_variables.h
|
fuzzer_pass_push_ids_through_variables.h
|
||||||
fuzzer_pass_replace_linear_algebra_instructions.h
|
fuzzer_pass_replace_linear_algebra_instructions.h
|
||||||
fuzzer_pass_replace_parameter_with_global.h
|
fuzzer_pass_replace_parameter_with_global.h
|
||||||
|
fuzzer_pass_replace_params_with_struct.h
|
||||||
fuzzer_pass_split_blocks.h
|
fuzzer_pass_split_blocks.h
|
||||||
fuzzer_pass_swap_commutable_operands.h
|
fuzzer_pass_swap_commutable_operands.h
|
||||||
fuzzer_pass_swap_conditional_branch_operands.h
|
fuzzer_pass_swap_conditional_branch_operands.h
|
||||||
@ -138,6 +139,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_id_with_synonym.h
|
transformation_replace_id_with_synonym.h
|
||||||
transformation_replace_linear_algebra_instruction.h
|
transformation_replace_linear_algebra_instruction.h
|
||||||
transformation_replace_parameter_with_global.h
|
transformation_replace_parameter_with_global.h
|
||||||
|
transformation_replace_params_with_struct.h
|
||||||
transformation_set_function_control.h
|
transformation_set_function_control.h
|
||||||
transformation_set_loop_control.h
|
transformation_set_loop_control.h
|
||||||
transformation_set_memory_operands_mask.h
|
transformation_set_memory_operands_mask.h
|
||||||
@ -196,6 +198,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
fuzzer_pass_push_ids_through_variables.cpp
|
fuzzer_pass_push_ids_through_variables.cpp
|
||||||
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
||||||
fuzzer_pass_replace_parameter_with_global.cpp
|
fuzzer_pass_replace_parameter_with_global.cpp
|
||||||
|
fuzzer_pass_replace_params_with_struct.cpp
|
||||||
fuzzer_pass_split_blocks.cpp
|
fuzzer_pass_split_blocks.cpp
|
||||||
fuzzer_pass_swap_commutable_operands.cpp
|
fuzzer_pass_swap_commutable_operands.cpp
|
||||||
fuzzer_pass_swap_conditional_branch_operands.cpp
|
fuzzer_pass_swap_conditional_branch_operands.cpp
|
||||||
@ -258,6 +261,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_id_with_synonym.cpp
|
transformation_replace_id_with_synonym.cpp
|
||||||
transformation_replace_linear_algebra_instruction.cpp
|
transformation_replace_linear_algebra_instruction.cpp
|
||||||
transformation_replace_parameter_with_global.cpp
|
transformation_replace_parameter_with_global.cpp
|
||||||
|
transformation_replace_params_with_struct.cpp
|
||||||
transformation_set_function_control.cpp
|
transformation_set_function_control.cpp
|
||||||
transformation_set_loop_control.cpp
|
transformation_set_loop_control.cpp
|
||||||
transformation_set_memory_operands_mask.cpp
|
transformation_set_memory_operands_mask.cpp
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
|
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
|
||||||
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
|
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
|
||||||
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
|
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
|
||||||
|
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
|
||||||
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
||||||
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
|
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
|
||||||
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
||||||
@ -288,6 +289,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||||||
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
||||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
|
||||||
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
|
transformation_sequence_out);
|
||||||
MaybeAddPass<FuzzerPassSplitBlocks>(
|
MaybeAddPass<FuzzerPassSplitBlocks>(
|
||||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
@ -82,6 +82,8 @@ const std::pair<uint32_t, uint32_t>
|
|||||||
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
||||||
30, 70};
|
30, 70};
|
||||||
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
|
||||||
|
20, 40};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
|
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
|
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
|
||||||
{10, 70};
|
{10, 70};
|
||||||
@ -99,6 +101,7 @@ const uint32_t kDefaultMaxNewArraySizeLimit = 100;
|
|||||||
// think whether there is a better limit on the maximum number of parameters.
|
// think whether there is a better limit on the maximum number of parameters.
|
||||||
const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
|
const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
|
||||||
const uint32_t kDefaultMaxNumberOfNewParameters = 15;
|
const uint32_t kDefaultMaxNumberOfNewParameters = 15;
|
||||||
|
const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5;
|
||||||
|
|
||||||
// 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.
|
||||||
@ -124,6 +127,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
|
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
|
||||||
max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
|
max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
|
||||||
max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
|
max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
|
||||||
|
max_number_of_parameters_replaced_with_struct_(
|
||||||
|
kGetDefaultMaxNumberOfParametersReplacedWithStruct),
|
||||||
go_deeper_in_constant_obfuscation_(
|
go_deeper_in_constant_obfuscation_(
|
||||||
kDefaultGoDeeperInConstantObfuscation) {
|
kDefaultGoDeeperInConstantObfuscation) {
|
||||||
chance_of_adding_access_chain_ =
|
chance_of_adding_access_chain_ =
|
||||||
@ -211,6 +216,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
||||||
chance_of_replacing_parameters_with_globals_ =
|
chance_of_replacing_parameters_with_globals_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
||||||
|
chance_of_replacing_parameters_with_struct_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
|
||||||
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
|
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
|
||||||
chance_of_swapping_conditional_branch_operands_ =
|
chance_of_swapping_conditional_branch_operands_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
|
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
|
||||||
|
@ -221,6 +221,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
||||||
return chance_of_replacing_parameters_with_globals_;
|
return chance_of_replacing_parameters_with_globals_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetChanceOfReplacingParametersWithStruct() {
|
||||||
|
return chance_of_replacing_parameters_with_struct_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
|
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
|
||||||
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
|
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
|
||||||
return chance_of_swapping_conditional_branch_operands_;
|
return chance_of_swapping_conditional_branch_operands_;
|
||||||
@ -237,6 +240,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetMaximumNumberOfFunctionParameters() {
|
uint32_t GetMaximumNumberOfFunctionParameters() {
|
||||||
return max_number_of_function_parameters_;
|
return max_number_of_function_parameters_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
|
||||||
|
return max_number_of_parameters_replaced_with_struct_;
|
||||||
|
}
|
||||||
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};
|
||||||
|
|
||||||
@ -278,6 +284,12 @@ class FuzzerContext {
|
|||||||
{1, std::min(max_number_of_new_parameters_,
|
{1, std::min(max_number_of_new_parameters_,
|
||||||
GetMaximumNumberOfFunctionParameters() - num_of_params)});
|
GetMaximumNumberOfFunctionParameters() - num_of_params)});
|
||||||
}
|
}
|
||||||
|
uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) {
|
||||||
|
assert(num_params != 0 && "A function must have parameters to replace");
|
||||||
|
return ChooseBetweenMinAndMax(
|
||||||
|
{1, std::min(num_params,
|
||||||
|
GetMaximumNumberOfParametersReplacedWithStruct())});
|
||||||
|
}
|
||||||
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;
|
||||||
@ -345,6 +357,7 @@ class FuzzerContext {
|
|||||||
uint32_t chance_of_replacing_id_with_synonym_;
|
uint32_t chance_of_replacing_id_with_synonym_;
|
||||||
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
||||||
uint32_t chance_of_replacing_parameters_with_globals_;
|
uint32_t chance_of_replacing_parameters_with_globals_;
|
||||||
|
uint32_t chance_of_replacing_parameters_with_struct_;
|
||||||
uint32_t chance_of_splitting_block_;
|
uint32_t chance_of_splitting_block_;
|
||||||
uint32_t chance_of_swapping_conditional_branch_operands_;
|
uint32_t chance_of_swapping_conditional_branch_operands_;
|
||||||
uint32_t chance_of_toggling_access_chain_instruction_;
|
uint32_t chance_of_toggling_access_chain_instruction_;
|
||||||
@ -359,6 +372,7 @@ class FuzzerContext {
|
|||||||
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_function_parameters_;
|
||||||
uint32_t max_number_of_new_parameters_;
|
uint32_t max_number_of_new_parameters_;
|
||||||
|
uint32_t max_number_of_parameters_replaced_with_struct_;
|
||||||
|
|
||||||
// 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.
|
||||||
|
115
source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
Normal file
115
source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// 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_replace_params_with_struct.h"
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_context.h"
|
||||||
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
|
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations)
|
||||||
|
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||||
|
transformations) {}
|
||||||
|
|
||||||
|
FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
|
||||||
|
default;
|
||||||
|
|
||||||
|
void FuzzerPassReplaceParamsWithStruct::Apply() {
|
||||||
|
for (const auto& function : *GetIRContext()->module()) {
|
||||||
|
auto params =
|
||||||
|
fuzzerutil::GetParameters(GetIRContext(), function.result_id());
|
||||||
|
|
||||||
|
if (params.empty() || fuzzerutil::FunctionIsEntryPoint(
|
||||||
|
GetIRContext(), function.result_id())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetFuzzerContext()->ChoosePercentage(
|
||||||
|
GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> parameter_index(params.size());
|
||||||
|
std::iota(parameter_index.begin(), parameter_index.end(), 0);
|
||||||
|
|
||||||
|
// Remove the indices of unsupported parameters.
|
||||||
|
auto new_end = std::remove_if(
|
||||||
|
parameter_index.begin(), parameter_index.end(),
|
||||||
|
[this, ¶ms](uint32_t index) {
|
||||||
|
const auto* type =
|
||||||
|
GetIRContext()->get_type_mgr()->GetType(params[index]->type_id());
|
||||||
|
assert(type && "Parameter has invalid type");
|
||||||
|
return !TransformationReplaceParamsWithStruct::
|
||||||
|
IsParameterTypeSupported(*type);
|
||||||
|
});
|
||||||
|
|
||||||
|
// std::remove_if changes the vector so that removed elements are placed at
|
||||||
|
// the end (i.e. [new_end, parameter_index.end()) is a range of removed
|
||||||
|
// elements). However, the size of the vector is not changed so we need to
|
||||||
|
// change that explicitly by popping those elements from the vector.
|
||||||
|
parameter_index.erase(new_end, parameter_index.end());
|
||||||
|
|
||||||
|
if (parameter_index.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select |num_replaced_params| parameters at random. We shuffle the vector
|
||||||
|
// of indices for randomization and shrink it to select first
|
||||||
|
// |num_replaced_params| parameters.
|
||||||
|
auto num_replaced_params = std::min<size_t>(
|
||||||
|
parameter_index.size(),
|
||||||
|
GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct(
|
||||||
|
static_cast<uint32_t>(params.size())));
|
||||||
|
|
||||||
|
GetFuzzerContext()->Shuffle(¶meter_index);
|
||||||
|
parameter_index.resize(num_replaced_params);
|
||||||
|
|
||||||
|
// Make sure OpTypeStruct exists in the module.
|
||||||
|
std::vector<uint32_t> component_type_ids;
|
||||||
|
for (auto index : parameter_index) {
|
||||||
|
component_type_ids.push_back(params[index]->type_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
FindOrCreateStructType(component_type_ids);
|
||||||
|
|
||||||
|
// Map parameters' indices to parameters' ids.
|
||||||
|
std::vector<uint32_t> parameter_id;
|
||||||
|
for (auto index : parameter_index) {
|
||||||
|
parameter_id.push_back(params[index]->result_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
|
||||||
|
for (const auto* inst :
|
||||||
|
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||||
|
caller_id_to_fresh_id[inst->result_id()] =
|
||||||
|
GetFuzzerContext()->GetFreshId();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyTransformation(TransformationReplaceParamsWithStruct(
|
||||||
|
parameter_id, GetFuzzerContext()->GetFreshId(),
|
||||||
|
GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
40
source/fuzz/fuzzer_pass_replace_params_with_struct.h
Normal file
40
source/fuzz/fuzzer_pass_replace_params_with_struct.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||||
|
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_pass.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
// Iterates over all functions in the module and randomly decides for each one
|
||||||
|
// whether to replace a subset of its parameters with a struct value.
|
||||||
|
class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
|
||||||
|
public:
|
||||||
|
FuzzerPassReplaceParamsWithStruct(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations);
|
||||||
|
|
||||||
|
~FuzzerPassReplaceParamsWithStruct() override;
|
||||||
|
|
||||||
|
void Apply() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
|
@ -728,6 +728,37 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||||
|
uint32_t function_id) {
|
||||||
|
assert(FindFunction(ir_context, function_id) &&
|
||||||
|
"|function_id| is not a result id of a function");
|
||||||
|
|
||||||
|
std::vector<opt::Instruction*> result;
|
||||||
|
ir_context->get_def_use_mgr()->ForEachUser(
|
||||||
|
function_id, [&result, function_id](opt::Instruction* inst) {
|
||||||
|
if (inst->opcode() == SpvOpFunctionCall &&
|
||||||
|
inst->GetSingleWordInOperand(0) == function_id) {
|
||||||
|
result.push_back(inst);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||||
|
uint32_t param_id) {
|
||||||
|
auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
|
||||||
|
assert(param_inst && "Parameter id is invalid");
|
||||||
|
|
||||||
|
for (auto& function : *ir_context->module()) {
|
||||||
|
if (InstructionIsFunctionParameter(param_inst, &function)) {
|
||||||
|
return &function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
|
void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
|
||||||
const std::vector<uint32_t>& type_ids) {
|
const std::vector<uint32_t>& type_ids) {
|
||||||
assert(result_id != 0 && "Result id can't be 0");
|
assert(result_id != 0 && "Result id can't be 0");
|
||||||
|
@ -277,6 +277,16 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
|||||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||||
uint32_t function_id);
|
uint32_t function_id);
|
||||||
|
|
||||||
|
// Returns all OpFunctionCall instructions that call a function with result id
|
||||||
|
// |function_id|.
|
||||||
|
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||||
|
uint32_t function_id);
|
||||||
|
|
||||||
|
// Returns a function that contains OpFunctionParameter instruction with result
|
||||||
|
// id |param_id|. Returns nullptr if the module has no such function.
|
||||||
|
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||||
|
uint32_t param_id);
|
||||||
|
|
||||||
// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
|
// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
|
||||||
// empty. It may not contain result ids of OpTypeFunction instructions.
|
// empty. It may not contain result ids of OpTypeFunction instructions.
|
||||||
// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
|
// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
|
||||||
|
@ -403,6 +403,7 @@ message Transformation {
|
|||||||
TransformationRecordSynonymousConstants record_synonymous_constants = 56;
|
TransformationRecordSynonymousConstants record_synonymous_constants = 56;
|
||||||
TransformationAddSynonym add_synonym = 57;
|
TransformationAddSynonym add_synonym = 57;
|
||||||
TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
|
TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
|
||||||
|
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
|
||||||
// Add additional option using the next available number.
|
// Add additional option using the next available number.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1313,6 +1314,31 @@ message TransformationReplaceLinearAlgebraInstruction {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TransformationReplaceParamsWithStruct {
|
||||||
|
|
||||||
|
// Replaces parameters of the function with a struct containing
|
||||||
|
// values of those parameters.
|
||||||
|
|
||||||
|
// Result ids of parameters to replace.
|
||||||
|
repeated uint32 parameter_id = 1;
|
||||||
|
|
||||||
|
// Fresh id for a new function type. This might be unused if the required type
|
||||||
|
// already exists in the module or if we can change the old type.
|
||||||
|
uint32 fresh_function_type_id = 2;
|
||||||
|
|
||||||
|
// Fresh id for a new struct function parameter to be used as a replacement.
|
||||||
|
uint32 fresh_parameter_id = 3;
|
||||||
|
|
||||||
|
// Fresh ids for struct objects containing values of replaced parameters.
|
||||||
|
// This map contains a fresh id for at least every result id of a relevant
|
||||||
|
// OpFunctionCall instruction.
|
||||||
|
//
|
||||||
|
// While maps are not fully deterministic, the way this map is used does not
|
||||||
|
// exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
|
||||||
|
map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
message TransformationSetFunctionControl {
|
message TransformationSetFunctionControl {
|
||||||
|
|
||||||
// A transformation that sets the function control operand of an OpFunction
|
// A transformation that sets the function control operand of an OpFunction
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||||
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
|
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
|
||||||
#include "source/fuzz/transformation_replace_parameter_with_global.h"
|
#include "source/fuzz/transformation_replace_parameter_with_global.h"
|
||||||
|
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||||
#include "source/fuzz/transformation_set_function_control.h"
|
#include "source/fuzz/transformation_set_function_control.h"
|
||||||
#include "source/fuzz/transformation_set_loop_control.h"
|
#include "source/fuzz/transformation_set_loop_control.h"
|
||||||
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
||||||
@ -222,6 +223,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||||||
kReplaceLinearAlgebraInstruction:
|
kReplaceLinearAlgebraInstruction:
|
||||||
return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
|
return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
|
||||||
message.replace_linear_algebra_instruction());
|
message.replace_linear_algebra_instruction());
|
||||||
|
case protobufs::Transformation::TransformationCase::
|
||||||
|
kReplaceParamsWithStruct:
|
||||||
|
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
||||||
|
message.replace_params_with_struct());
|
||||||
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
|
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
|
||||||
return MakeUnique<TransformationSetFunctionControl>(
|
return MakeUnique<TransformationSetFunctionControl>(
|
||||||
message.set_function_control());
|
message.set_function_control());
|
||||||
|
@ -91,29 +91,29 @@ void TransformationPermuteFunctionParameters::Apply(
|
|||||||
fuzzerutil::GetFunctionType(ir_context, function);
|
fuzzerutil::GetFunctionType(ir_context, function);
|
||||||
assert(old_function_type_inst && "Function must have a valid type");
|
assert(old_function_type_inst && "Function must have a valid type");
|
||||||
|
|
||||||
// Change function's type
|
std::vector<uint32_t> type_ids = {
|
||||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) {
|
old_function_type_inst->GetSingleWordInOperand(0)};
|
||||||
|
for (auto index : message_.permutation()) {
|
||||||
|
// +1 since the first operand to OpTypeFunction is a return type.
|
||||||
|
type_ids.push_back(
|
||||||
|
old_function_type_inst->GetSingleWordInOperand(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change function's type.
|
||||||
|
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1 &&
|
||||||
|
fuzzerutil::FindFunctionType(ir_context, type_ids) == 0) {
|
||||||
// If only the current function uses |old_function_type_inst| - change it
|
// If only the current function uses |old_function_type_inst| - change it
|
||||||
// in-place.
|
// in-place. We can only do that if the module doesn't contain
|
||||||
opt::Instruction::OperandList permuted_operands = {
|
// a function type with the permuted order of operands.
|
||||||
std::move(old_function_type_inst->GetInOperand(0))};
|
opt::Instruction::OperandList permuted_operands;
|
||||||
for (auto index : message_.permutation()) {
|
for (auto id : type_ids) {
|
||||||
// +1 since the first operand to OpTypeFunction is a return type.
|
// +1 since the first operand to OpTypeFunction is a return type.
|
||||||
permuted_operands.push_back(
|
permuted_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
|
||||||
std::move(old_function_type_inst->GetInOperand(index + 1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
old_function_type_inst->SetInOperands(std::move(permuted_operands));
|
old_function_type_inst->SetInOperands(std::move(permuted_operands));
|
||||||
} else {
|
} else {
|
||||||
// Either use an existing type or create a new one.
|
// Either use an existing type or create a new one.
|
||||||
std::vector<uint32_t> type_ids = {
|
|
||||||
old_function_type_inst->GetSingleWordInOperand(0)};
|
|
||||||
for (auto index : message_.permutation()) {
|
|
||||||
// +1 since the first operand to OpTypeFunction is a return type.
|
|
||||||
type_ids.push_back(
|
|
||||||
old_function_type_inst->GetSingleWordInOperand(index + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
function->DefInst().SetInOperand(
|
function->DefInst().SetInOperand(
|
||||||
1, {fuzzerutil::FindOrCreateFunctionType(
|
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||||
ir_context, message_.function_type_fresh_id(), type_ids)});
|
ir_context, message_.function_type_fresh_id(), type_ids)});
|
||||||
@ -146,24 +146,18 @@ void TransformationPermuteFunctionParameters::Apply(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fix all OpFunctionCall instructions
|
// Fix all OpFunctionCall instructions
|
||||||
ir_context->get_def_use_mgr()->ForEachUser(
|
for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||||
&function->DefInst(), [this](opt::Instruction* call) {
|
opt::Instruction::OperandList call_operands = {
|
||||||
if (call->opcode() != SpvOpFunctionCall ||
|
call->GetInOperand(0) // Function id
|
||||||
call->GetSingleWordInOperand(0) != message_.function_id()) {
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt::Instruction::OperandList call_operands = {
|
for (auto index : message_.permutation()) {
|
||||||
call->GetInOperand(0) // Function id
|
// Take function id into account
|
||||||
};
|
call_operands.push_back(call->GetInOperand(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
for (auto index : message_.permutation()) {
|
call->SetInOperands(std::move(call_operands));
|
||||||
// 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
|
// Make sure our changes are analyzed
|
||||||
ir_context->InvalidateAnalysesExceptFor(
|
ir_context->InvalidateAnalysesExceptFor(
|
||||||
|
@ -20,23 +20,6 @@
|
|||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
namespace fuzz {
|
namespace fuzz {
|
||||||
namespace {
|
|
||||||
|
|
||||||
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
|
||||||
uint32_t param_id) {
|
|
||||||
auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
|
|
||||||
assert(param_inst && "Parameter id is invalid");
|
|
||||||
|
|
||||||
for (auto& function : *ir_context->module()) {
|
|
||||||
if (fuzzerutil::InstructionIsFunctionParameter(param_inst, &function)) {
|
|
||||||
return &function;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TransformationReplaceParameterWithGlobal::
|
TransformationReplaceParameterWithGlobal::
|
||||||
TransformationReplaceParameterWithGlobal(
|
TransformationReplaceParameterWithGlobal(
|
||||||
@ -63,8 +46,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that function exists and is not an entry point.
|
// Check that function exists and is not an entry point.
|
||||||
const auto* function =
|
const auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||||
GetFunctionFromParameterId(ir_context, message_.parameter_id());
|
ir_context, message_.parameter_id());
|
||||||
if (!function ||
|
if (!function ||
|
||||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||||
return false;
|
return false;
|
||||||
@ -124,8 +107,8 @@ void TransformationReplaceParameterWithGlobal::Apply(
|
|||||||
message_.global_variable_fresh_id());
|
message_.global_variable_fresh_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* function =
|
auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||||
GetFunctionFromParameterId(ir_context, message_.parameter_id());
|
ir_context, message_.parameter_id());
|
||||||
assert(function && "Function must exist");
|
assert(function && "Function must exist");
|
||||||
|
|
||||||
// Insert an OpLoad instruction right after OpVariable instructions.
|
// Insert an OpLoad instruction right after OpVariable instructions.
|
||||||
@ -159,29 +142,23 @@ void TransformationReplaceParameterWithGlobal::Apply(
|
|||||||
"Parameter must exist in the function");
|
"Parameter must exist in the function");
|
||||||
|
|
||||||
// Update all relevant OpFunctionCall instructions.
|
// Update all relevant OpFunctionCall instructions.
|
||||||
ir_context->get_def_use_mgr()->ForEachUser(
|
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||||
function->result_id(),
|
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
|
||||||
[ir_context, parameter_index, this](opt::Instruction* inst) {
|
"Can't insert OpStore right before the function call");
|
||||||
if (inst->opcode() != SpvOpFunctionCall) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
|
// Insert an OpStore before the OpFunctionCall. +1 since the first
|
||||||
"Can't insert OpStore right before the function call");
|
// operand of OpFunctionCall is an id of the function.
|
||||||
|
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpStore, 0, 0,
|
||||||
|
opt::Instruction::OperandList{
|
||||||
|
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
|
||||||
|
{SPV_OPERAND_TYPE_ID,
|
||||||
|
{inst->GetSingleWordInOperand(parameter_index + 1)}}}));
|
||||||
|
|
||||||
// Insert an OpStore before the OpFunctionCall. +1 since the first
|
// +1 since the first operand of OpFunctionCall is an id of the
|
||||||
// operand of OpFunctionCall is an id of the function.
|
// function.
|
||||||
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
inst->RemoveInOperand(parameter_index + 1);
|
||||||
ir_context, SpvOpStore, 0, 0,
|
}
|
||||||
opt::Instruction::OperandList{
|
|
||||||
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
|
|
||||||
{SPV_OPERAND_TYPE_ID,
|
|
||||||
{inst->GetSingleWordInOperand(parameter_index + 1)}}}));
|
|
||||||
|
|
||||||
// +1 since the first operand of OpFunctionCall is an id of the
|
|
||||||
// function.
|
|
||||||
inst->RemoveInOperand(parameter_index + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove the parameter from the function.
|
// Remove the parameter from the function.
|
||||||
function->RemoveParameter(message_.parameter_id());
|
function->RemoveParameter(message_.parameter_id());
|
||||||
@ -201,7 +178,8 @@ void TransformationReplaceParameterWithGlobal::Apply(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
|
||||||
|
fuzzerutil::FindFunctionType(ir_context, type_ids) == 0) {
|
||||||
// Change the old type in place. +1 since the first operand is the result
|
// Change the old type in place. +1 since the first operand is the result
|
||||||
// type id of the function.
|
// type id of the function.
|
||||||
old_function_type->RemoveInOperand(parameter_index + 1);
|
old_function_type->RemoveInOperand(parameter_index + 1);
|
||||||
|
319
source/fuzz/transformation_replace_params_with_struct.cpp
Normal file
319
source/fuzz/transformation_replace_params_with_struct.cpp
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
// 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_replace_params_with_struct.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||||
|
const protobufs::TransformationReplaceParamsWithStruct& message)
|
||||||
|
: message_(message) {}
|
||||||
|
|
||||||
|
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||||
|
const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
|
||||||
|
uint32_t fresh_parameter_id,
|
||||||
|
const std::unordered_map<uint32_t, uint32_t>&
|
||||||
|
caller_id_to_fresh_composite_id) {
|
||||||
|
message_.set_fresh_function_type_id(fresh_function_type_id);
|
||||||
|
message_.set_fresh_parameter_id(fresh_parameter_id);
|
||||||
|
|
||||||
|
for (auto id : parameter_id) {
|
||||||
|
message_.add_parameter_id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
message_.mutable_caller_id_to_fresh_composite_id()->insert(
|
||||||
|
caller_id_to_fresh_composite_id.begin(),
|
||||||
|
caller_id_to_fresh_composite_id.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||||
|
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||||
|
std::vector<uint32_t> parameter_id(message_.parameter_id().begin(),
|
||||||
|
message_.parameter_id().end());
|
||||||
|
|
||||||
|
// Check that |parameter_id| is neither empty nor it has duplicates.
|
||||||
|
if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All ids must correspond to valid parameters of the same function.
|
||||||
|
// The function can't be an entry-point function.
|
||||||
|
|
||||||
|
// fuzzerutil::GetFunctionFromParameterId requires a valid id.
|
||||||
|
if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* function =
|
||||||
|
fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]);
|
||||||
|
if (!function ||
|
||||||
|
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute all ids of the function's parameters.
|
||||||
|
std::unordered_set<uint32_t> all_parameter_ids;
|
||||||
|
for (const auto* param :
|
||||||
|
fuzzerutil::GetParameters(ir_context, function->result_id())) {
|
||||||
|
all_parameter_ids.insert(param->result_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all elements in |parameter_id| are valid.
|
||||||
|
for (auto id : parameter_id) {
|
||||||
|
// fuzzerutil::GetFunctionFromParameterId requires a valid id.
|
||||||
|
if (!ir_context->get_def_use_mgr()->GetDef(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that |id| is a result id of one of the |function|'s parameters.
|
||||||
|
if (!all_parameter_ids.count(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the parameter with result id |id| has supported type.
|
||||||
|
const auto* type = ir_context->get_type_mgr()->GetType(
|
||||||
|
fuzzerutil::GetTypeId(ir_context, id));
|
||||||
|
assert(type && "Parameter has invalid type");
|
||||||
|
if (!IsParameterTypeSupported(*type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We already know that the function has at least |parameter_id.size()|
|
||||||
|
// parameters.
|
||||||
|
|
||||||
|
// Check that a relevant OpTypeStruct exists in the module.
|
||||||
|
if (!MaybeGetRequiredStructType(ir_context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that |callee_id_to_fresh_composite_id| is valid.
|
||||||
|
for (const auto* inst :
|
||||||
|
fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||||
|
// Check that the callee is present in the map. It's ok if the map contains
|
||||||
|
// more ids that there are callees (those ids will not be used).
|
||||||
|
if (!message_.caller_id_to_fresh_composite_id().contains(
|
||||||
|
inst->result_id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all fresh ids are unique and fresh.
|
||||||
|
std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
|
||||||
|
message_.fresh_parameter_id()};
|
||||||
|
|
||||||
|
for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
|
||||||
|
fresh_ids.push_back(entry.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !fuzzerutil::HasDuplicates(fresh_ids) &&
|
||||||
|
std::all_of(fresh_ids.begin(), fresh_ids.end(),
|
||||||
|
[ir_context](uint32_t id) {
|
||||||
|
return fuzzerutil::IsFreshId(ir_context, id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformationReplaceParamsWithStruct::Apply(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||||
|
auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||||
|
ir_context, message_.parameter_id(0));
|
||||||
|
assert(function &&
|
||||||
|
"All parameters' ids should've been checked in the IsApplicable");
|
||||||
|
|
||||||
|
// Get a type id of the OpTypeStruct used as a type id of the new parameter.
|
||||||
|
auto struct_type_id = MaybeGetRequiredStructType(ir_context);
|
||||||
|
assert(struct_type_id &&
|
||||||
|
"IsApplicable should've guaranteed that this value isn't equal to 0");
|
||||||
|
|
||||||
|
// Add new parameter to the function.
|
||||||
|
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpFunctionParameter, struct_type_id,
|
||||||
|
message_.fresh_parameter_id(), opt::Instruction::OperandList()));
|
||||||
|
|
||||||
|
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
|
||||||
|
|
||||||
|
// Compute indices of replaced parameters. This will be used to adjust
|
||||||
|
// OpFunctionCall instructions and create OpCompositeConstruct instructions at
|
||||||
|
// every call site.
|
||||||
|
std::vector<uint32_t> indices_of_replaced_params;
|
||||||
|
{
|
||||||
|
// We want to destroy |params| after the loop because it will contain
|
||||||
|
// dangling pointers when we remove parameters from the function.
|
||||||
|
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
|
||||||
|
for (auto id : message_.parameter_id()) {
|
||||||
|
auto it = std::find_if(params.begin(), params.end(),
|
||||||
|
[id](const opt::Instruction* param) {
|
||||||
|
return param->result_id() == id;
|
||||||
|
});
|
||||||
|
assert(it != params.end() && "Parameter's id is invalid");
|
||||||
|
indices_of_replaced_params.push_back(
|
||||||
|
static_cast<uint32_t>(it - params.begin()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update all function calls.
|
||||||
|
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||||
|
// Create a list of operands for the OpCompositeConstruct instruction.
|
||||||
|
opt::Instruction::OperandList composite_components;
|
||||||
|
for (auto index : indices_of_replaced_params) {
|
||||||
|
// +1 since the first in operand to OpFunctionCall is the result id of
|
||||||
|
// the function.
|
||||||
|
composite_components.emplace_back(
|
||||||
|
std::move(inst->GetInOperand(index + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove arguments from the function call. We do it in a separate loop
|
||||||
|
// and in reverse order to make sure we have removed correct operands.
|
||||||
|
for (auto it = indices_of_replaced_params.rbegin();
|
||||||
|
it != indices_of_replaced_params.rend(); ++it) {
|
||||||
|
// +1 since the first in operand to OpFunctionCall is the result id of
|
||||||
|
// the function.
|
||||||
|
inst->RemoveInOperand(*it + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert OpCompositeConstruct before the function call.
|
||||||
|
auto fresh_composite_id =
|
||||||
|
message_.caller_id_to_fresh_composite_id().at(inst->result_id());
|
||||||
|
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
|
||||||
|
std::move(composite_components)));
|
||||||
|
|
||||||
|
// Add a new operand to the OpFunctionCall instruction.
|
||||||
|
inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
|
||||||
|
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert OpCompositeExtract instructions into the entry point block of the
|
||||||
|
// function and remove replaced parameters.
|
||||||
|
for (int i = 0; i < message_.parameter_id_size(); ++i) {
|
||||||
|
const auto* param_inst =
|
||||||
|
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i));
|
||||||
|
assert(param_inst && "Parameter id is invalid");
|
||||||
|
|
||||||
|
// Skip all OpVariable instructions.
|
||||||
|
auto iter = function->begin()->begin();
|
||||||
|
while (iter != function->begin()->end() &&
|
||||||
|
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
|
||||||
|
iter)) {
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
|
||||||
|
iter) &&
|
||||||
|
"Can't extract parameter's value from the structure");
|
||||||
|
|
||||||
|
// Insert OpCompositeExtract instructions to unpack parameters' values from
|
||||||
|
// the struct type.
|
||||||
|
iter.InsertBefore(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpCompositeExtract, param_inst->type_id(),
|
||||||
|
param_inst->result_id(),
|
||||||
|
opt::Instruction::OperandList{
|
||||||
|
{SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
|
||||||
|
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
|
||||||
|
|
||||||
|
function->RemoveParameter(param_inst->result_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update function's type.
|
||||||
|
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||||
|
assert(old_function_type && "Function has invalid type");
|
||||||
|
|
||||||
|
std::vector<uint32_t> type_ids = {
|
||||||
|
// Result type of the function.
|
||||||
|
old_function_type->GetSingleWordInOperand(0)};
|
||||||
|
|
||||||
|
// +1 since the first in operand to OpTypeFunction is the result type id
|
||||||
|
// of the function.
|
||||||
|
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
|
||||||
|
if (std::find(indices_of_replaced_params.begin(),
|
||||||
|
indices_of_replaced_params.end(),
|
||||||
|
i - 1) == indices_of_replaced_params.end()) {
|
||||||
|
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_ids.push_back(struct_type_id);
|
||||||
|
|
||||||
|
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
|
||||||
|
fuzzerutil::FindFunctionType(ir_context, type_ids) == 0) {
|
||||||
|
// Update |old_function_type| in place.
|
||||||
|
opt::Instruction::OperandList replaced_operands;
|
||||||
|
for (auto id : type_ids) {
|
||||||
|
replaced_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
|
||||||
|
}
|
||||||
|
|
||||||
|
old_function_type->SetInOperands(std::move(replaced_operands));
|
||||||
|
|
||||||
|
// Make sure domination rules are satisfied.
|
||||||
|
old_function_type->RemoveFromList();
|
||||||
|
ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
|
||||||
|
} else {
|
||||||
|
// Create a new function type or use an existing one.
|
||||||
|
function->DefInst().SetInOperand(
|
||||||
|
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||||
|
ir_context, message_.fresh_function_type_id(), type_ids)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure our changes are analyzed
|
||||||
|
ir_context->InvalidateAnalysesExceptFor(
|
||||||
|
opt::IRContext::Analysis::kAnalysisNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage()
|
||||||
|
const {
|
||||||
|
protobufs::Transformation result;
|
||||||
|
*result.mutable_replace_params_with_struct() = message_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported(
|
||||||
|
const opt::analysis::Type& param_type) {
|
||||||
|
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||||
|
// Consider adding support for more types of parameters.
|
||||||
|
switch (param_type.kind()) {
|
||||||
|
case opt::analysis::Type::kBool:
|
||||||
|
case opt::analysis::Type::kInteger:
|
||||||
|
case opt::analysis::Type::kFloat:
|
||||||
|
case opt::analysis::Type::kArray:
|
||||||
|
case opt::analysis::Type::kVector:
|
||||||
|
case opt::analysis::Type::kMatrix:
|
||||||
|
return true;
|
||||||
|
case opt::analysis::Type::kStruct:
|
||||||
|
return std::all_of(param_type.AsStruct()->element_types().begin(),
|
||||||
|
param_type.AsStruct()->element_types().end(),
|
||||||
|
[](const opt::analysis::Type* type) {
|
||||||
|
return IsParameterTypeSupported(*type);
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
|
||||||
|
opt::IRContext* ir_context) const {
|
||||||
|
std::vector<uint32_t> component_type_ids;
|
||||||
|
for (auto id : message_.parameter_id()) {
|
||||||
|
component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
83
source/fuzz/transformation_replace_params_with_struct.h
Normal file
83
source/fuzz/transformation_replace_params_with_struct.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// 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_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||||
|
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#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 TransformationReplaceParamsWithStruct : public Transformation {
|
||||||
|
public:
|
||||||
|
explicit TransformationReplaceParamsWithStruct(
|
||||||
|
const protobufs::TransformationReplaceParamsWithStruct& message);
|
||||||
|
|
||||||
|
TransformationReplaceParamsWithStruct(
|
||||||
|
const std::vector<uint32_t>& parameter_id,
|
||||||
|
uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
|
||||||
|
const std::unordered_map<uint32_t, uint32_t>&
|
||||||
|
caller_id_to_fresh_composite_id);
|
||||||
|
|
||||||
|
// - Each element of |parameter_id| is a valid result id of some
|
||||||
|
// OpFunctionParameter instruction. All parameter ids must correspond to
|
||||||
|
// parameters of the same function. That function may not be an entry-point
|
||||||
|
// function.
|
||||||
|
// - Types of all parameters must be supported by this transformation (see
|
||||||
|
// IsParameterTypeSupported method).
|
||||||
|
// - |parameter_id| may not be empty or contain duplicates.
|
||||||
|
// - There must exist an OpTypeStruct instruction containing types of all
|
||||||
|
// replaced parameters. Type of the i'th component of the struct is equal
|
||||||
|
// to the type of the instruction with result id |parameter_id[i]|.
|
||||||
|
// - |caller_id_to_fresh_composite_id| should contain a key for at least every
|
||||||
|
// result id of an OpFunctionCall instruction that calls the function.
|
||||||
|
// - |fresh_function_type_id|, |fresh_parameter_id|,
|
||||||
|
// |caller_id_to_fresh_composite_id| are all fresh and unique ids.
|
||||||
|
bool IsApplicable(
|
||||||
|
opt::IRContext* ir_context,
|
||||||
|
const TransformationContext& transformation_context) const override;
|
||||||
|
|
||||||
|
// - Creates a new function parameter with result id |fresh_parameter_id|.
|
||||||
|
// Parameter's type is OpTypeStruct with each components type equal to the
|
||||||
|
// type of the replaced parameter.
|
||||||
|
// - OpCompositeConstruct with result id from |fresh_composite_id| is inserted
|
||||||
|
// before each OpFunctionCall instruction.
|
||||||
|
// - OpCompositeExtract with result id equal to the result id of the replaced
|
||||||
|
// parameter is created in the function.
|
||||||
|
void Apply(opt::IRContext* ir_context,
|
||||||
|
TransformationContext* transformation_context) const override;
|
||||||
|
|
||||||
|
protobufs::Transformation ToMessage() const override;
|
||||||
|
|
||||||
|
// Returns true if parameter's type is supported by this transformation.
|
||||||
|
static bool IsParameterTypeSupported(const opt::analysis::Type& param_type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Returns a result id of the OpTypeStruct instruction required by this
|
||||||
|
// transformation (see docs on the IsApplicable method to learn more).
|
||||||
|
uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
|
||||||
|
|
||||||
|
protobufs::TransformationReplaceParamsWithStruct message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
@ -71,6 +71,7 @@ if (${SPIRV_BUILD_FUZZER})
|
|||||||
transformation_replace_constant_with_uniform_test.cpp
|
transformation_replace_constant_with_uniform_test.cpp
|
||||||
transformation_replace_id_with_synonym_test.cpp
|
transformation_replace_id_with_synonym_test.cpp
|
||||||
transformation_replace_linear_algebra_instruction_test.cpp
|
transformation_replace_linear_algebra_instruction_test.cpp
|
||||||
|
transformation_replace_params_with_struct_test.cpp
|
||||||
transformation_set_function_control_test.cpp
|
transformation_set_function_control_test.cpp
|
||||||
transformation_set_loop_control_test.cpp
|
transformation_set_loop_control_test.cpp
|
||||||
transformation_set_memory_operands_mask_test.cpp
|
transformation_set_memory_operands_mask_test.cpp
|
||||||
|
@ -90,6 +90,11 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
%84 = OpConstant %14 5
|
%84 = OpConstant %14 5
|
||||||
%90 = OpConstant %6 3
|
%90 = OpConstant %6 3
|
||||||
%98 = OpConstant %6 4
|
%98 = OpConstant %6 4
|
||||||
|
%206 = OpTypeFunction %2 %14 %16
|
||||||
|
%223 = OpTypeFunction %2 %6 %8
|
||||||
|
%224 = OpTypeFunction %2 %8 %6
|
||||||
|
%233 = OpTypeFunction %2 %42 %24
|
||||||
|
%234 = OpTypeFunction %2 %24 %42
|
||||||
%4 = OpFunction %2 None %3
|
%4 = OpFunction %2 None %3
|
||||||
%5 = OpLabel
|
%5 = OpLabel
|
||||||
%66 = OpVariable %15 Function
|
%66 = OpVariable %15 Function
|
||||||
@ -148,6 +153,8 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
%70 = OpLabel
|
%70 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; adjust type of the function in-place
|
||||||
%12 = OpFunction %8 None %9
|
%12 = OpFunction %8 None %9
|
||||||
%10 = OpFunctionParameter %7
|
%10 = OpFunctionParameter %7
|
||||||
%11 = OpFunctionParameter %7
|
%11 = OpFunctionParameter %7
|
||||||
@ -192,6 +199,53 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
OpReturnValue %61
|
OpReturnValue %61
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; create a new function type
|
||||||
|
%200 = OpFunction %2 None %206
|
||||||
|
%207 = OpFunctionParameter %14
|
||||||
|
%208 = OpFunctionParameter %16
|
||||||
|
%202 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%203 = OpFunction %2 None %206
|
||||||
|
%209 = OpFunctionParameter %14
|
||||||
|
%210 = OpFunctionParameter %16
|
||||||
|
%205 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; reuse an existing function type
|
||||||
|
%211 = OpFunction %2 None %223
|
||||||
|
%212 = OpFunctionParameter %6
|
||||||
|
%213 = OpFunctionParameter %8
|
||||||
|
%214 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%215 = OpFunction %2 None %224
|
||||||
|
%216 = OpFunctionParameter %8
|
||||||
|
%217 = OpFunctionParameter %6
|
||||||
|
%218 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%219 = OpFunction %2 None %224
|
||||||
|
%220 = OpFunctionParameter %8
|
||||||
|
%221 = OpFunctionParameter %6
|
||||||
|
%222 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; don't adjust the type of the function if it creates a duplicate
|
||||||
|
%225 = OpFunction %2 None %233
|
||||||
|
%226 = OpFunctionParameter %42
|
||||||
|
%227 = OpFunctionParameter %24
|
||||||
|
%228 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%229 = OpFunction %2 None %234
|
||||||
|
%230 = OpFunctionParameter %24
|
||||||
|
%231 = OpFunctionParameter %42
|
||||||
|
%232 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
)";
|
)";
|
||||||
|
|
||||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
@ -250,6 +304,27 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
transformation.Apply(context.get(), &transformation_context);
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
ASSERT_TRUE(IsValid(env, context.get()));
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
TransformationPermuteFunctionParameters transformation(200, 107, {1, 0});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationPermuteFunctionParameters transformation(219, 108, {1, 0});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationPermuteFunctionParameters transformation(229, 109, {1, 0});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
std::string after_transformation = R"(
|
std::string after_transformation = R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -320,6 +395,12 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
%84 = OpConstant %14 5
|
%84 = OpConstant %14 5
|
||||||
%90 = OpConstant %6 3
|
%90 = OpConstant %6 3
|
||||||
%98 = OpConstant %6 4
|
%98 = OpConstant %6 4
|
||||||
|
%206 = OpTypeFunction %2 %14 %16
|
||||||
|
%223 = OpTypeFunction %2 %6 %8
|
||||||
|
%224 = OpTypeFunction %2 %8 %6
|
||||||
|
%233 = OpTypeFunction %2 %42 %24
|
||||||
|
%234 = OpTypeFunction %2 %24 %42
|
||||||
|
%107 = OpTypeFunction %2 %16 %14
|
||||||
%4 = OpFunction %2 None %3
|
%4 = OpFunction %2 None %3
|
||||||
%5 = OpLabel
|
%5 = OpLabel
|
||||||
%66 = OpVariable %15 Function
|
%66 = OpVariable %15 Function
|
||||||
@ -421,6 +502,48 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
|||||||
%61 = OpFOrdLessThan %24 %59 %60
|
%61 = OpFOrdLessThan %24 %59 %60
|
||||||
OpReturnValue %61
|
OpReturnValue %61
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
%200 = OpFunction %2 None %107
|
||||||
|
%208 = OpFunctionParameter %16
|
||||||
|
%207 = OpFunctionParameter %14
|
||||||
|
%202 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%203 = OpFunction %2 None %206
|
||||||
|
%209 = OpFunctionParameter %14
|
||||||
|
%210 = OpFunctionParameter %16
|
||||||
|
%205 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%211 = OpFunction %2 None %223
|
||||||
|
%212 = OpFunctionParameter %6
|
||||||
|
%213 = OpFunctionParameter %8
|
||||||
|
%214 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%215 = OpFunction %2 None %224
|
||||||
|
%216 = OpFunctionParameter %8
|
||||||
|
%217 = OpFunctionParameter %6
|
||||||
|
%218 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%219 = OpFunction %2 None %223
|
||||||
|
%221 = OpFunctionParameter %6
|
||||||
|
%220 = OpFunctionParameter %8
|
||||||
|
%222 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%225 = OpFunction %2 None %233
|
||||||
|
%226 = OpFunctionParameter %42
|
||||||
|
%227 = OpFunctionParameter %24
|
||||||
|
%228 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%229 = OpFunction %2 None %233
|
||||||
|
%231 = OpFunctionParameter %42
|
||||||
|
%230 = OpFunctionParameter %24
|
||||||
|
%232 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
)";
|
)";
|
||||||
|
|
||||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||||
|
@ -38,6 +38,10 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%10 = OpTypeVector %8 2
|
%10 = OpTypeVector %8 2
|
||||||
%11 = OpTypePointer Private %10
|
%11 = OpTypePointer Private %10
|
||||||
%12 = OpTypeBool
|
%12 = OpTypeBool
|
||||||
|
%71 = OpTypeFunction %2 %6
|
||||||
|
%83 = OpTypeFunction %2 %6 %12
|
||||||
|
%93 = OpTypeFunction %2 %10
|
||||||
|
%94 = OpTypeFunction %2 %8 %10
|
||||||
%40 = OpTypePointer Function %12
|
%40 = OpTypePointer Function %12
|
||||||
%13 = OpTypeStruct %6 %8
|
%13 = OpTypeStruct %6 %8
|
||||||
%14 = OpTypePointer Private %13
|
%14 = OpTypePointer Private %13
|
||||||
@ -53,6 +57,8 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
|
%33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; adjust type of the function in-place
|
||||||
%20 = OpFunction %2 None %15
|
%20 = OpFunction %2 None %15
|
||||||
%16 = OpFunctionParameter %6
|
%16 = OpFunctionParameter %6
|
||||||
%17 = OpFunctionParameter %8
|
%17 = OpFunctionParameter %8
|
||||||
@ -63,6 +69,45 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%21 = OpLabel
|
%21 = OpLabel
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; reuse an existing function type
|
||||||
|
%70 = OpFunction %2 None %71
|
||||||
|
%72 = OpFunctionParameter %6
|
||||||
|
%73 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%74 = OpFunction %2 None %71
|
||||||
|
%75 = OpFunctionParameter %6
|
||||||
|
%76 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; create a new function type
|
||||||
|
%77 = OpFunction %2 None %83
|
||||||
|
%78 = OpFunctionParameter %6
|
||||||
|
%84 = OpFunctionParameter %12
|
||||||
|
%79 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%80 = OpFunction %2 None %83
|
||||||
|
%81 = OpFunctionParameter %6
|
||||||
|
%85 = OpFunctionParameter %12
|
||||||
|
%82 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; don't adjust the type of the function if it creates a duplicate
|
||||||
|
%86 = OpFunction %2 None %93
|
||||||
|
%87 = OpFunctionParameter %10
|
||||||
|
%89 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%90 = OpFunction %2 None %94
|
||||||
|
%91 = OpFunctionParameter %8
|
||||||
|
%95 = OpFunctionParameter %10
|
||||||
|
%92 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
)";
|
)";
|
||||||
|
|
||||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||||
@ -131,6 +176,26 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
transformation.IsApplicable(context.get(), transformation_context));
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
transformation.Apply(context.get(), &transformation_context);
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParameterWithGlobal transformation(58, 75, 59);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParameterWithGlobal transformation(60, 81, 61);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParameterWithGlobal transformation(62, 91, 63);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
std::string expected_shader = R"(
|
std::string expected_shader = R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -150,6 +215,10 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%10 = OpTypeVector %8 2
|
%10 = OpTypeVector %8 2
|
||||||
%11 = OpTypePointer Private %10
|
%11 = OpTypePointer Private %10
|
||||||
%12 = OpTypeBool
|
%12 = OpTypeBool
|
||||||
|
%71 = OpTypeFunction %2 %6
|
||||||
|
%83 = OpTypeFunction %2 %6 %12
|
||||||
|
%93 = OpTypeFunction %2 %10
|
||||||
|
%94 = OpTypeFunction %2 %8 %10
|
||||||
%40 = OpTypePointer Function %12
|
%40 = OpTypePointer Function %12
|
||||||
%13 = OpTypeStruct %6 %8
|
%13 = OpTypeStruct %6 %8
|
||||||
%14 = OpTypePointer Private %13
|
%14 = OpTypePointer Private %13
|
||||||
@ -163,6 +232,10 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%53 = OpVariable %9 Private %23
|
%53 = OpVariable %9 Private %23
|
||||||
%55 = OpVariable %11 Private %26
|
%55 = OpVariable %11 Private %26
|
||||||
%57 = OpVariable %14 Private %28
|
%57 = OpVariable %14 Private %28
|
||||||
|
%59 = OpVariable %7 Private %22
|
||||||
|
%61 = OpVariable %7 Private %22
|
||||||
|
%60 = OpTypeFunction %2 %12
|
||||||
|
%63 = OpVariable %9 Private %23
|
||||||
%4 = OpFunction %2 None %3
|
%4 = OpFunction %2 None %3
|
||||||
%5 = OpLabel
|
%5 = OpLabel
|
||||||
%41 = OpVariable %40 Function %27
|
%41 = OpVariable %40 Function %27
|
||||||
@ -183,6 +256,39 @@ TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
|
|||||||
%16 = OpLoad %6 %51
|
%16 = OpLoad %6 %51
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
%70 = OpFunction %2 None %71
|
||||||
|
%72 = OpFunctionParameter %6
|
||||||
|
%73 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%74 = OpFunction %2 None %3
|
||||||
|
%76 = OpLabel
|
||||||
|
%75 = OpLoad %6 %59
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%77 = OpFunction %2 None %83
|
||||||
|
%78 = OpFunctionParameter %6
|
||||||
|
%84 = OpFunctionParameter %12
|
||||||
|
%79 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%80 = OpFunction %2 None %60
|
||||||
|
%85 = OpFunctionParameter %12
|
||||||
|
%82 = OpLabel
|
||||||
|
%81 = OpLoad %6 %61
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%86 = OpFunction %2 None %93
|
||||||
|
%87 = OpFunctionParameter %10
|
||||||
|
%89 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%90 = OpFunction %2 None %93
|
||||||
|
%95 = OpFunctionParameter %10
|
||||||
|
%92 = OpLabel
|
||||||
|
%91 = OpLoad %8 %63
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
)";
|
)";
|
||||||
|
|
||||||
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
|
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
|
||||||
|
339
test/fuzz/transformation_replace_params_with_struct_test.cpp
Normal file
339
test/fuzz/transformation_replace_params_with_struct_test.cpp
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
// 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_replace_params_with_struct.h"
|
||||||
|
|
||||||
|
#include "test/fuzz/fuzz_test_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(TransformationReplaceParamsWithStructTest, 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
|
||||||
|
OpMemberDecorate %13 0 RelaxedPrecision
|
||||||
|
OpDecorate %16 RelaxedPrecision
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%6 = OpTypeInt 32 1
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%7 = OpTypePointer Private %6
|
||||||
|
%8 = OpTypeFloat 32
|
||||||
|
%9 = OpTypePointer Private %8
|
||||||
|
%10 = OpTypeVector %8 2
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpTypeBool
|
||||||
|
%51 = OpTypeFunction %2 %12
|
||||||
|
%64 = OpTypeStruct %6
|
||||||
|
%63 = OpTypeFunction %2 %64
|
||||||
|
%65 = OpTypeFunction %2 %6
|
||||||
|
%75 = OpTypeStruct %8
|
||||||
|
%76 = OpTypeFunction %2 %75
|
||||||
|
%77 = OpTypeFunction %2 %8
|
||||||
|
%40 = OpTypePointer Function %12
|
||||||
|
%13 = OpTypeStruct %6 %8
|
||||||
|
%45 = OpTypeStruct %6 %10 %13
|
||||||
|
%46 = OpTypeStruct %12
|
||||||
|
%47 = OpTypeStruct %8 %45 %46
|
||||||
|
%14 = OpTypePointer Private %13
|
||||||
|
%15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
|
||||||
|
%22 = OpConstant %6 0
|
||||||
|
%23 = OpConstant %8 0
|
||||||
|
%26 = OpConstantComposite %10 %23 %23
|
||||||
|
%27 = OpConstantTrue %12
|
||||||
|
%28 = OpConstantComposite %13 %22 %23
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%41 = OpVariable %40 Function %27
|
||||||
|
%33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; adjust type of the function in-place
|
||||||
|
%20 = OpFunction %2 None %15
|
||||||
|
%16 = OpFunctionParameter %6
|
||||||
|
%17 = OpFunctionParameter %8
|
||||||
|
%18 = OpFunctionParameter %10
|
||||||
|
%19 = OpFunctionParameter %13
|
||||||
|
%42 = OpFunctionParameter %40
|
||||||
|
%43 = OpFunctionParameter %12
|
||||||
|
%21 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; create a new function type
|
||||||
|
%50 = OpFunction %2 None %51
|
||||||
|
%52 = OpFunctionParameter %12
|
||||||
|
%53 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%54 = OpFunction %2 None %51
|
||||||
|
%55 = OpFunctionParameter %12
|
||||||
|
%56 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; reuse an existing function type
|
||||||
|
%57 = OpFunction %2 None %63
|
||||||
|
%58 = OpFunctionParameter %64
|
||||||
|
%59 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%60 = OpFunction %2 None %65
|
||||||
|
%61 = OpFunctionParameter %6
|
||||||
|
%62 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%66 = OpFunction %2 None %65
|
||||||
|
%67 = OpFunctionParameter %6
|
||||||
|
%68 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
; don't adjust the type of the function if it creates a duplicate
|
||||||
|
%69 = OpFunction %2 None %76
|
||||||
|
%70 = OpFunctionParameter %75
|
||||||
|
%71 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%72 = OpFunction %2 None %77
|
||||||
|
%73 = OpFunctionParameter %8
|
||||||
|
%74 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
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);
|
||||||
|
|
||||||
|
// |parameter_id| is empty.
|
||||||
|
ASSERT_FALSE(
|
||||||
|
TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// |parameter_id| has duplicates.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// |parameter_id| has invalid values.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Parameter's belong to different functions.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Parameter has unsupported type.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// OpTypeStruct does not exist in the module.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// |caller_id_to_fresh_composite_id| misses values.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// All fresh ids must be unique.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
|
||||||
|
{{33, 90}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
|
||||||
|
{{33, 92}, {90, 92}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// All 'fresh' ids must be fresh.
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
|
||||||
|
{{33, 33}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33,
|
||||||
|
{{33, 92}, {90, 93}})
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91,
|
||||||
|
{{33, 92}, {90, 93}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({43}, 93, 94,
|
||||||
|
{{33, 95}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97,
|
||||||
|
{{33, 98}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}});
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
OpMemberDecorate %13 0 RelaxedPrecision
|
||||||
|
OpDecorate %16 RelaxedPrecision
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%6 = OpTypeInt 32 1
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%7 = OpTypePointer Private %6
|
||||||
|
%8 = OpTypeFloat 32
|
||||||
|
%9 = OpTypePointer Private %8
|
||||||
|
%10 = OpTypeVector %8 2
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpTypeBool
|
||||||
|
%51 = OpTypeFunction %2 %12
|
||||||
|
%64 = OpTypeStruct %6
|
||||||
|
%63 = OpTypeFunction %2 %64
|
||||||
|
%65 = OpTypeFunction %2 %6
|
||||||
|
%75 = OpTypeStruct %8
|
||||||
|
%76 = OpTypeFunction %2 %75
|
||||||
|
%77 = OpTypeFunction %2 %8
|
||||||
|
%40 = OpTypePointer Function %12
|
||||||
|
%13 = OpTypeStruct %6 %8
|
||||||
|
%45 = OpTypeStruct %6 %10 %13
|
||||||
|
%46 = OpTypeStruct %12
|
||||||
|
%47 = OpTypeStruct %8 %45 %46
|
||||||
|
%14 = OpTypePointer Private %13
|
||||||
|
%22 = OpConstant %6 0
|
||||||
|
%23 = OpConstant %8 0
|
||||||
|
%26 = OpConstantComposite %10 %23 %23
|
||||||
|
%27 = OpConstantTrue %12
|
||||||
|
%28 = OpConstantComposite %13 %22 %23
|
||||||
|
%15 = OpTypeFunction %2 %40 %47
|
||||||
|
%99 = OpTypeFunction %2 %46
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%41 = OpVariable %40 Function %27
|
||||||
|
%92 = OpCompositeConstruct %45 %22 %26 %28
|
||||||
|
%95 = OpCompositeConstruct %46 %27
|
||||||
|
%98 = OpCompositeConstruct %47 %23 %92 %95
|
||||||
|
%33 = OpFunctionCall %2 %20 %41 %98
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%20 = OpFunction %2 None %15
|
||||||
|
%42 = OpFunctionParameter %40
|
||||||
|
%97 = OpFunctionParameter %47
|
||||||
|
%21 = OpLabel
|
||||||
|
%94 = OpCompositeExtract %46 %97 2
|
||||||
|
%91 = OpCompositeExtract %45 %97 1
|
||||||
|
%17 = OpCompositeExtract %8 %97 0
|
||||||
|
%43 = OpCompositeExtract %12 %94 0
|
||||||
|
%19 = OpCompositeExtract %13 %91 2
|
||||||
|
%18 = OpCompositeExtract %10 %91 1
|
||||||
|
%16 = OpCompositeExtract %6 %91 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%50 = OpFunction %2 None %51
|
||||||
|
%52 = OpFunctionParameter %12
|
||||||
|
%53 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%54 = OpFunction %2 None %99
|
||||||
|
%100 = OpFunctionParameter %46
|
||||||
|
%56 = OpLabel
|
||||||
|
%55 = OpCompositeExtract %12 %100 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%57 = OpFunction %2 None %63
|
||||||
|
%58 = OpFunctionParameter %64
|
||||||
|
%59 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%60 = OpFunction %2 None %63
|
||||||
|
%102 = OpFunctionParameter %64
|
||||||
|
%62 = OpLabel
|
||||||
|
%61 = OpCompositeExtract %6 %102 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%66 = OpFunction %2 None %65
|
||||||
|
%67 = OpFunctionParameter %6
|
||||||
|
%68 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%69 = OpFunction %2 None %76
|
||||||
|
%70 = OpFunctionParameter %75
|
||||||
|
%71 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%72 = OpFunction %2 None %76
|
||||||
|
%104 = OpFunctionParameter %75
|
||||||
|
%74 = OpLabel
|
||||||
|
%73 = OpCompositeExtract %8 %104 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
Loading…
Reference in New Issue
Block a user