spirv-fuzz: TransformationReplaceParameterWithGlobal (#3434)

Fixes #3432.
This commit is contained in:
Vasyl Teliman 2020-07-09 13:03:49 +03:00 committed by GitHub
parent 11946e640c
commit de56c34bd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 683 additions and 7 deletions

View File

@ -71,6 +71,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_replace_parameter_with_global.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
fuzzer_pass_swap_conditional_branch_operands.h
@ -131,6 +132,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
transformation_replace_linear_algebra_instruction.h
transformation_replace_parameter_with_global.h
transformation_set_function_control.h
transformation_set_loop_control.h
transformation_set_memory_operands_mask.h
@ -185,6 +187,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_replace_parameter_with_global.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
fuzzer_pass_swap_conditional_branch_operands.cpp
@ -244,6 +247,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
transformation_replace_linear_algebra_instruction.cpp
transformation_replace_parameter_with_global.cpp
transformation_set_function_control.cpp
transformation_set_loop_control.cpp
transformation_set_memory_operands_mask.cpp

View File

@ -54,6 +54,7 @@
#include "source/fuzz/fuzzer_pass_permute_phi_operands.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_parameter_with_global.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
@ -276,6 +277,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@ -76,6 +76,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
{10, 70};
@ -198,6 +200,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_replacing_linear_algebra_instructions_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
chance_of_replacing_parameters_with_globals_ =
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
chance_of_swapping_conditional_branch_operands_ =
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);

View File

@ -210,6 +210,9 @@ class FuzzerContext {
uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
return chance_of_replacing_linear_algebra_instructions_;
}
uint32_t GetChanceOfReplacingParametersWithGlobals() {
return chance_of_replacing_parameters_with_globals_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
return chance_of_swapping_conditional_branch_operands_;
@ -329,6 +332,7 @@ class FuzzerContext {
uint32_t chance_of_pushing_id_through_variable_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
uint32_t chance_of_replacing_parameters_with_globals_;
uint32_t chance_of_splitting_block_;
uint32_t chance_of_swapping_conditional_branch_operands_;
uint32_t chance_of_toggling_access_chain_instruction_;

View File

@ -0,0 +1,92 @@
// 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_parameter_with_global.h"
#include <numeric>
#include <vector>
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
namespace spvtools {
namespace fuzz {
FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() =
default;
void FuzzerPassReplaceParameterWithGlobal::Apply() {
for (const auto& function : *GetIRContext()->module()) {
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
function.result_id())) {
continue;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) {
continue;
}
auto params =
fuzzerutil::GetParameters(GetIRContext(), function.result_id());
// Make sure at least one parameter can be replaced. Also checks that the
// function has at least one parameter.
if (std::none_of(params.begin(), params.end(),
[this](const opt::Instruction* param) {
const auto* param_type =
GetIRContext()->get_type_mgr()->GetType(
param->type_id());
assert(param_type && "Parameter has invalid type");
return TransformationReplaceParameterWithGlobal::
CanReplaceFunctionParameterType(*param_type);
})) {
continue;
}
// Select id of a parameter to replace.
const opt::Instruction* replaced_param = nullptr;
const opt::analysis::Type* param_type = nullptr;
do {
replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(&params);
param_type =
GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
assert(param_type && "Parameter has invalid type");
} while (!TransformationReplaceParameterWithGlobal::
CanReplaceFunctionParameterType(*param_type));
assert(replaced_param && "Unable to find a parameter to replace");
// Make sure type id for the global variable exists in the module.
FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
// Make sure initializer for the global variable exists in the module.
FindOrCreateZeroConstant(replaced_param->type_id());
ApplyTransformation(TransformationReplaceParameterWithGlobal(
GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),
GetFuzzerContext()->GetFreshId()));
}
}
} // namespace fuzz
} // namespace spvtools

View 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_PARAMETER_WITH_GLOBAL_H_
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Iterates over all non-entry-point functions in the module and randomly
// replaces a parameter with a global variable.
class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass {
public:
FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassReplaceParameterWithGlobal() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_

View File

@ -614,6 +614,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
context, SpvOpVariable, type_id, result_id, std::move(operands)));
AddVariableIdToEntryPointInterfaces(context, result_id);
UpdateModuleIdBound(context, result_id);
}
void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
@ -644,6 +645,8 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
{SPV_OPERAND_TYPE_ID, {initializer_id}}}));
UpdateModuleIdBound(context, result_id);
}
bool HasDuplicates(const std::vector<uint32_t>& arr) {

View File

@ -225,7 +225,8 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
// Adds a global variable with storage class |storage_class| to the module, with
// type |type_id| and either no initializer or |initializer_id| as an
// initializer, depending on whether |initializer_id| is 0. The global variable
// has result id |result_id|.
// has result id |result_id|. Updates module's id bound to accommodate for
// |result_id|.
//
// - |type_id| must be the id of a pointer type with the same storage class as
// |storage_class|.
@ -239,6 +240,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
// Adds an instruction to the start of |function_id|, of the form:
// |result_id| = OpVariable |type_id| Function |initializer_id|.
// Updates module's id bound to accommodate for |result_id|.
//
// - |type_id| must be the id of a pointer type with Function storage class.
// - |initializer_id| must be the id of a constant with the same type as the

View File

@ -384,6 +384,7 @@ message Transformation {
TransformationAddCopyMemory add_copy_memory = 53;
TransformationInvertComparisonOperator invert_comparison_operator = 54;
TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55;
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56;
// Add additional option using the next available number.
}
}
@ -1122,6 +1123,23 @@ message TransformationPushIdThroughVariable {
}
message TransformationReplaceParameterWithGlobal {
// Removes parameter with result id |parameter_id| from its function
// and creates a global variable to pass its value to the function instead.
// Fresh id for a new function type. This might not be used if a required
// function type already exists or if we can change the old function type.
uint32 function_type_fresh_id = 2;
// Result id of the OpFunctionParameter instruction to remove.
uint32 parameter_id = 3;
// Fresh id of a global variable used to pass parameter's value to the function.
uint32 global_variable_fresh_id = 4;
}
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with

View File

@ -62,6 +62,7 @@
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
#include "source/fuzz/transformation_set_function_control.h"
#include "source/fuzz/transformation_set_loop_control.h"
#include "source/fuzz/transformation_set_memory_operands_mask.h"
@ -193,6 +194,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
return MakeUnique<TransformationPushIdThroughVariable>(
message.push_id_through_variable());
case protobufs::Transformation::TransformationCase::
kReplaceParameterWithGlobal:
return MakeUnique<TransformationReplaceParameterWithGlobal>(
message.replace_parameter_with_global());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(

View File

@ -98,8 +98,6 @@ void TransformationAddGlobalVariable::Apply(
static_cast<SpvStorageClass>(message_.storage_class()),
message_.initializer_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
message_.fresh_id());

View File

@ -74,8 +74,6 @@ void TransformationAddLocalVariable::Apply(
message_.type_id(), message_.function_id(),
message_.initializer_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
message_.fresh_id());

View File

@ -127,8 +127,6 @@ void TransformationPushIdThroughVariable::Apply(
message_.initializer_id());
}
fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
// Stores value id to variable id.
FindInstruction(message_.instruction_descriptor(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(

View File

@ -0,0 +1,245 @@
// 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_parameter_with_global.h"
#include <vector>
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
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(
const protobufs::TransformationReplaceParameterWithGlobal& message)
: message_(message) {}
TransformationReplaceParameterWithGlobal::
TransformationReplaceParameterWithGlobal(
uint32_t function_type_fresh_id, uint32_t parameter_id,
uint32_t global_variable_fresh_id) {
message_.set_function_type_fresh_id(function_type_fresh_id);
message_.set_parameter_id(parameter_id);
message_.set_global_variable_fresh_id(global_variable_fresh_id);
}
bool TransformationReplaceParameterWithGlobal::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// Check that |parameter_id| is valid.
const auto* param_inst =
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) {
return false;
}
// Check that function exists and is not an entry point.
const auto* function =
GetFunctionFromParameterId(ir_context, message_.parameter_id());
if (!function ||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
return false;
}
// We already know that the function has at least one parameter -
// |parameter_id|.
// Check that replaced parameter has valid type.
const auto* param_type =
ir_context->get_type_mgr()->GetType(param_inst->type_id());
assert(param_type && "Parameter has invalid type");
if (!CanReplaceFunctionParameterType(*param_type)) {
return false;
}
// Check that initializer for the global variable exists in the module.
if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) ==
0) {
return false;
}
// Check that pointer type for the global variable exists in the module.
if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
SpvStorageClassPrivate)) {
return false;
}
return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
fuzzerutil::IsFreshId(ir_context,
message_.global_variable_fresh_id()) &&
message_.function_type_fresh_id() !=
message_.global_variable_fresh_id();
}
void TransformationReplaceParameterWithGlobal::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
const auto* param_inst =
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
assert(param_inst && "Parameter must exist");
// Create global variable to store parameter's value.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
// Mark the global variable's pointee as irrelevant if replaced parameter is
// irrelevant.
fuzzerutil::AddGlobalVariable(
ir_context, message_.global_variable_fresh_id(),
fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
SpvStorageClassPrivate),
SpvStorageClassPrivate,
fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()));
auto* function =
GetFunctionFromParameterId(ir_context, message_.parameter_id());
assert(function && "Function must exist");
// Insert an OpLoad instruction right after OpVariable instructions.
auto it = function->begin()->begin();
while (it != function->begin()->end() &&
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) {
++it;
}
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) &&
"Can't insert OpLoad or OpCopyMemory into the first basic block of "
"the function");
it.InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}}));
// Calculate the index of the replaced parameter (we need to know this to
// remove operands from the OpFunctionCall).
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
auto parameter_index = static_cast<uint32_t>(params.size());
for (uint32_t i = 0, n = static_cast<uint32_t>(params.size()); i < n; ++i) {
if (params[i]->result_id() == message_.parameter_id()) {
parameter_index = i;
break;
}
}
assert(parameter_index != params.size() &&
"Parameter must exist in the function");
// Update all relevant OpFunctionCall instructions.
ir_context->get_def_use_mgr()->ForEachUser(
function->result_id(),
[ir_context, parameter_index, this](opt::Instruction* inst) {
if (inst->opcode() != SpvOpFunctionCall) {
return;
}
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
"Can't insert OpStore right before the function call");
// Insert an OpStore before the OpFunctionCall. +1 since the first
// 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)}}}));
// +1 since the first operand of OpFunctionCall is an id of the
// function.
inst->RemoveInOperand(parameter_index + 1);
});
// Remove the parameter from the function.
function->RemoveParameter(message_.parameter_id());
// Update function's type.
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
assert(old_function_type && "Function has invalid type");
// Preemptively add function's return type id.
std::vector<uint32_t> type_ids = {
old_function_type->GetSingleWordInOperand(0)};
// +1 and -1 since the first operand is the return type id.
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
if (i - 1 != parameter_index) {
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
}
}
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
// Change the old type in place. +1 since the first operand is the result
// type id of the function.
old_function_type->RemoveInOperand(parameter_index + 1);
} else {
// Find an existing or create a new function type.
function->DefInst().SetInOperand(
1, {fuzzerutil::FindOrCreateFunctionType(
ir_context, message_.function_type_fresh_id(), type_ids)});
}
// Make sure our changes are analyzed
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_replace_parameter_with_global() = message_;
return result;
}
bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
const opt::analysis::Type& type) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
// Think about other type instructions we can add here.
switch (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::kMatrix:
case opt::analysis::Type::kVector:
return true;
case opt::analysis::Type::kStruct:
return std::all_of(
type.AsStruct()->element_types().begin(),
type.AsStruct()->element_types().end(),
[](const opt::analysis::Type* element_type) {
return CanReplaceFunctionParameterType(*element_type);
});
default:
return false;
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,67 @@
// 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_PARAMETER_WITH_GLOBAL_H_
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationReplaceParameterWithGlobal : public Transformation {
public:
explicit TransformationReplaceParameterWithGlobal(
const protobufs::TransformationReplaceParameterWithGlobal& message);
TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id,
uint32_t parameter_id,
uint32_t global_variable_fresh_id);
// - |function_type_fresh_id| is a fresh id.
// - |parameter_id| is the result id of the parameter to replace.
// - |global_variable_fresh_id| is a fresh id.
// - |function_type_fresh_id| is not equal to |global_variable_fresh_id|.
// - the function that contains |parameter_id| may not be an entry-point
// function.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// - Removes parameter with result id |parameter_id| from its function
// - Adds a global variable to store the value for the parameter
// - Add an OpStore instruction before each function call to
// store parameter's value into the variable
// - Adds OpLoad at the beginning of the function to load the
// value from the variable into the old parameter's id
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
// Returns true if the type of the parameter is supported by this
// transformation.
static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type);
private:
protobufs::TransformationReplaceParameterWithGlobal message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_

View File

@ -64,6 +64,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_permute_function_parameters_test.cpp
transformation_permute_phi_operands_test.cpp
transformation_push_id_through_variable_test.cpp
transformation_replace_parameter_with_global_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,193 @@
// 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_parameter_with_global.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationReplaceParameterWithGlobalTest, 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
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Private %6
%8 = OpTypeFloat 32
%9 = OpTypePointer Private %8
%10 = OpTypeVector %8 2
%11 = OpTypePointer Private %10
%12 = OpTypeBool
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%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
%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
)";
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 invalid.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 50, 51)
.IsApplicable(context.get(), transformation_context));
// Parameter id is not a result id of an OpFunctionParameter instruction.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 21, 51)
.IsApplicable(context.get(), transformation_context));
// Parameter has unsupported type.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 42, 51)
.IsApplicable(context.get(), transformation_context));
// Initializer for a global variable doesn't exist in the module.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
.IsApplicable(context.get(), transformation_context));
// Pointer type for a global variable doesn't exist in the module.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
.IsApplicable(context.get(), transformation_context));
// Function type id is not fresh.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(16, 16, 51)
.IsApplicable(context.get(), transformation_context));
// Global variable id is not fresh.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 16)
.IsApplicable(context.get(), transformation_context));
// Function type fresh id and global variable fresh id are equal.
ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 50)
.IsApplicable(context.get(), transformation_context));
{
TransformationReplaceParameterWithGlobal transformation(50, 16, 51);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
}
{
TransformationReplaceParameterWithGlobal transformation(52, 17, 53);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
}
{
TransformationReplaceParameterWithGlobal transformation(54, 18, 55);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
}
{
TransformationReplaceParameterWithGlobal transformation(56, 19, 57);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
}
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
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Private %6
%8 = OpTypeFloat 32
%9 = OpTypePointer Private %8
%10 = OpTypeVector %8 2
%11 = OpTypePointer Private %10
%12 = OpTypeBool
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%14 = OpTypePointer Private %13
%15 = OpTypeFunction %2 %40 %12
%22 = OpConstant %6 0
%23 = OpConstant %8 0
%26 = OpConstantComposite %10 %23 %23
%27 = OpConstantTrue %12
%28 = OpConstantComposite %13 %22 %23
%51 = OpVariable %7 Private %22
%53 = OpVariable %9 Private %23
%55 = OpVariable %11 Private %26
%57 = OpVariable %14 Private %28
%4 = OpFunction %2 None %3
%5 = OpLabel
%41 = OpVariable %40 Function %27
OpStore %51 %22
OpStore %53 %23
OpStore %55 %26
OpStore %57 %28
%33 = OpFunctionCall %2 %20 %41 %27
OpReturn
OpFunctionEnd
%20 = OpFunction %2 None %15
%42 = OpFunctionParameter %40
%43 = OpFunctionParameter %12
%21 = OpLabel
%19 = OpLoad %13 %57
%18 = OpLoad %10 %55
%17 = OpLoad %8 %53
%16 = OpLoad %6 %51
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools