mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 21:30:07 +00:00
spirv-fuzz: TransformationReplaceParameterWithGlobal (#3434)
Fixes #3432.
This commit is contained in:
parent
11946e640c
commit
de56c34bd6
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
92
source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
Normal file
92
source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
Normal 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(¶ms);
|
||||
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
|
40
source/fuzz/fuzzer_pass_replace_parameter_with_global.h
Normal file
40
source/fuzz/fuzzer_pass_replace_parameter_with_global.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_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_
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>(
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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>(
|
||||
|
245
source/fuzz/transformation_replace_parameter_with_global.cpp
Normal file
245
source/fuzz/transformation_replace_parameter_with_global.cpp
Normal 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
|
67
source/fuzz/transformation_replace_parameter_with_global.h
Normal file
67
source/fuzz/transformation_replace_parameter_with_global.h
Normal 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_
|
@ -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
|
||||
|
193
test/fuzz/transformation_replace_parameter_with_global_test.cpp
Normal file
193
test/fuzz/transformation_replace_parameter_with_global_test.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user