mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-23 04:00:05 +00:00
spirv-fuzz: Add one parameter at a time (#3469)
Fixes #3467. Fixes #3468.
This commit is contained in:
parent
fc0dc3a9c7
commit
ab10489a02
@ -95,7 +95,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.h
|
||||
transformation_add_local_variable.h
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_parameters.h
|
||||
transformation_add_parameter.h
|
||||
transformation_add_spec_constant_op.h
|
||||
transformation_add_type_array.h
|
||||
transformation_add_type_boolean.h
|
||||
@ -202,7 +202,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.cpp
|
||||
transformation_add_local_variable.cpp
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_parameters.cpp
|
||||
transformation_add_parameter.cpp
|
||||
transformation_add_spec_constant_op.cpp
|
||||
transformation_add_type_array.cpp
|
||||
transformation_add_type_boolean.cpp
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -57,50 +57,17 @@ void FuzzerPassAddParameters::Apply() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* type_inst =
|
||||
fuzzerutil::GetFunctionType(GetIRContext(), &function);
|
||||
assert(type_inst);
|
||||
|
||||
// -1 because we don't take return type into account.
|
||||
auto num_old_parameters = type_inst->NumInOperands() - 1;
|
||||
auto num_new_parameters =
|
||||
GetFuzzerContext()->GetRandomNumberOfNewParameters(
|
||||
GetNumberOfParameters(function));
|
||||
|
||||
std::vector<uint32_t> all_types(num_old_parameters);
|
||||
std::vector<uint32_t> new_types(num_new_parameters);
|
||||
std::vector<uint32_t> parameter_ids(num_new_parameters);
|
||||
std::vector<uint32_t> constant_ids(num_new_parameters);
|
||||
|
||||
// Get type ids for old parameters.
|
||||
for (uint32_t i = 0; i < num_old_parameters; ++i) {
|
||||
// +1 since we don't take return type into account.
|
||||
all_types[i] = type_inst->GetSingleWordInOperand(i + 1);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_new_parameters; ++i) {
|
||||
// Get type ids for new parameters.
|
||||
new_types[i] =
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
|
||||
|
||||
// Create constants to initialize new parameters from.
|
||||
constant_ids[i] = FindOrCreateZeroConstant(new_types[i]);
|
||||
ApplyTransformation(TransformationAddParameter(
|
||||
function.result_id(), GetFuzzerContext()->GetFreshId(),
|
||||
FindOrCreateZeroConstant(
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(
|
||||
type_candidates)]),
|
||||
GetFuzzerContext()->GetFreshId()));
|
||||
}
|
||||
|
||||
// Append new parameters to the old ones.
|
||||
all_types.insert(all_types.end(), new_types.begin(), new_types.end());
|
||||
|
||||
// Generate result ids for new parameters.
|
||||
for (auto& id : parameter_ids) {
|
||||
id = GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
|
||||
auto result_type_id = type_inst->GetSingleWordInOperand(0);
|
||||
ApplyTransformation(TransformationAddParameters(
|
||||
function.result_id(),
|
||||
FindOrCreateFunctionType(result_type_id, all_types),
|
||||
std::move(new_types), std::move(parameter_ids),
|
||||
std::move(constant_ids)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,20 +61,9 @@ void FuzzerPassPermuteFunctionParameters::Apply() {
|
||||
std::iota(permutation.begin(), permutation.end(), 0);
|
||||
GetFuzzerContext()->Shuffle(&permutation);
|
||||
|
||||
// Create a new OpFunctionType instruction with permuted arguments
|
||||
// if needed
|
||||
auto result_type_id = function_type->GetSingleWordInOperand(0);
|
||||
std::vector<uint32_t> argument_ids;
|
||||
|
||||
for (auto index : permutation) {
|
||||
// +1 to take function's return type into account
|
||||
argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
|
||||
}
|
||||
|
||||
// Apply our transformation
|
||||
ApplyTransformation(TransformationPermuteFunctionParameters(
|
||||
function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
|
||||
permutation));
|
||||
function_id, GetFuzzerContext()->GetFreshId(), permutation));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -590,6 +590,8 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, SpvStorageClass storage_class,
|
||||
uint32_t initializer_id) {
|
||||
// Check various preconditions.
|
||||
assert(result_id != 0 && "Result id can't be 0");
|
||||
|
||||
assert((storage_class == SpvStorageClassPrivate ||
|
||||
storage_class == SpvStorageClassWorkgroup) &&
|
||||
"Variable's storage class must be either Private or Workgroup");
|
||||
@ -631,6 +633,8 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, uint32_t function_id,
|
||||
uint32_t initializer_id) {
|
||||
// Check various preconditions.
|
||||
assert(result_id != 0 && "Result id can't be 0");
|
||||
|
||||
auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
|
||||
(void)type_inst; // Variable becomes unused in release mode.
|
||||
assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
|
||||
@ -687,6 +691,44 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& type_ids) {
|
||||
assert(result_id != 0 && "Result id can't be 0");
|
||||
assert(!type_ids.empty() &&
|
||||
"OpTypeFunction always has at least one operand - function's return "
|
||||
"type");
|
||||
assert(IsNonFunctionTypeId(ir_context, type_ids[0]) &&
|
||||
"Return type must not be a function");
|
||||
|
||||
for (size_t i = 1; i < type_ids.size(); ++i) {
|
||||
const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]);
|
||||
(void)param_type; // Make compiler happy in release mode.
|
||||
assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() &&
|
||||
"Function parameter can't have a function or void type");
|
||||
}
|
||||
|
||||
opt::Instruction::OperandList operands;
|
||||
operands.reserve(type_ids.size());
|
||||
for (auto id : type_ids) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
|
||||
}
|
||||
|
||||
ir_context->AddType(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands)));
|
||||
|
||||
UpdateModuleIdBound(ir_context, result_id);
|
||||
}
|
||||
|
||||
uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
|
||||
uint32_t result_id,
|
||||
const std::vector<uint32_t>& type_ids) {
|
||||
if (auto existing_id = FindFunctionType(ir_context, type_ids)) {
|
||||
return existing_id;
|
||||
}
|
||||
AddFunctionType(ir_context, result_id, type_ids);
|
||||
return result_id;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -266,6 +266,24 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
uint32_t function_id);
|
||||
|
||||
// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
|
||||
// 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|.
|
||||
// |result_id| may not equal to 0. Updates module's id bound to accommodate for
|
||||
// |result_id|.
|
||||
void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& type_ids);
|
||||
|
||||
// Returns a result id of an OpTypeFunction instruction in the module. Creates a
|
||||
// new instruction if required and returns |result_id|. type_ids| may not be
|
||||
// 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|.
|
||||
// |result_id| must not be equal to 0. Updates module's id bound to accommodate
|
||||
// for |result_id|.
|
||||
uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
|
||||
uint32_t result_id,
|
||||
const std::vector<uint32_t>& type_ids);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -380,7 +380,7 @@ message Transformation {
|
||||
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
|
||||
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
|
||||
TransformationPermutePhiOperands permute_phi_operands = 51;
|
||||
TransformationAddParameters add_parameters = 52;
|
||||
TransformationAddParameter add_parameter = 52;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@ -624,24 +624,25 @@ message TransformationAddNoContractionDecoration {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddParameters {
|
||||
message TransformationAddParameter {
|
||||
|
||||
// Adds new parameters into the function.
|
||||
// Adds a new parameter into the function.
|
||||
|
||||
// Result id of the function to add parameters to.
|
||||
uint32 function_id = 1;
|
||||
|
||||
// New type of the function.
|
||||
uint32 new_type_id = 2;
|
||||
// Fresh id for a new parameter.
|
||||
uint32 parameter_fresh_id = 2;
|
||||
|
||||
// Type ids of parameters to add to the function.
|
||||
repeated uint32 new_parameter_type = 3;
|
||||
// Result id of the instruction, used to initializer new parameter
|
||||
// in function calls. Type id of that instruction is the type id of the parameter.
|
||||
// It may not be OpTypeVoid.
|
||||
uint32 initializer_id = 3;
|
||||
|
||||
// Result ids for new parameters.
|
||||
repeated uint32 new_parameter_id = 4;
|
||||
|
||||
// Constants to initialize new parameters from.
|
||||
repeated uint32 constant_id = 5;
|
||||
// A 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 = 4;
|
||||
|
||||
}
|
||||
|
||||
@ -1011,12 +1012,10 @@ message TransformationPermuteFunctionParameters {
|
||||
// Function, whose parameters will be permuted
|
||||
uint32 function_id = 1;
|
||||
|
||||
// |new_type_id| is a result id of a valid OpTypeFunction instruction.
|
||||
// New type is valid if:
|
||||
// - it has the same number of operands as the old one
|
||||
// - function's result type is the same as the old one
|
||||
// - function's arguments are permuted according to |permutation| vector
|
||||
uint32 new_type_id = 2;
|
||||
// Fresh id for a new type of the function. 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;
|
||||
|
||||
// An array of size |n|, where |n| is a number of arguments to a function
|
||||
// with |function_id|. For each i: 0 <= permutation[i] < n.
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
#include "source/fuzz/transformation_add_spec_constant_op.h"
|
||||
#include "source/fuzz/transformation_add_type_array.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
@ -115,8 +115,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kAddNoContractionDecoration:
|
||||
return MakeUnique<TransformationAddNoContractionDecoration>(
|
||||
message.add_no_contraction_decoration());
|
||||
case protobufs::Transformation::TransformationCase::kAddParameters:
|
||||
return MakeUnique<TransformationAddParameters>(message.add_parameters());
|
||||
case protobufs::Transformation::TransformationCase::kAddParameter:
|
||||
return MakeUnique<TransformationAddParameter>(message.add_parameter());
|
||||
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
|
||||
return MakeUnique<TransformationAddSpecConstantOp>(
|
||||
message.add_spec_constant_op());
|
||||
|
135
source/fuzz/transformation_add_parameter.cpp
Normal file
135
source/fuzz/transformation_add_parameter.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
|
||||
#include <source/spirv_constant.h>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddParameter::TransformationAddParameter(
|
||||
const protobufs::TransformationAddParameter& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddParameter::TransformationAddParameter(
|
||||
uint32_t function_id, uint32_t parameter_fresh_id, uint32_t initializer_id,
|
||||
uint32_t function_type_fresh_id) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_parameter_fresh_id(parameter_fresh_id);
|
||||
message_.set_initializer_id(initializer_id);
|
||||
message_.set_function_type_fresh_id(function_type_fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationAddParameter::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that function exists
|
||||
const auto* function =
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
if (!function ||
|
||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |initializer_id| is valid.
|
||||
const auto* initializer_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
|
||||
|
||||
if (!initializer_inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that initializer's type is valid.
|
||||
const auto* initializer_type =
|
||||
ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
|
||||
|
||||
if (!initializer_type || initializer_type->AsVoid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
|
||||
fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
|
||||
message_.parameter_fresh_id() != message_.function_type_fresh_id();
|
||||
}
|
||||
|
||||
void TransformationAddParameter::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Find the function that will be transformed
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
auto parameter_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
|
||||
|
||||
// Add new parameters to the function.
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFunctionParameter, parameter_type_id,
|
||||
message_.parameter_fresh_id(), opt::Instruction::OperandList()));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(), [this](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != message_.function_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// it would be good to mark this usage of |id| as irrelevant, so that
|
||||
// we can perform some interesting transformations on it later.
|
||||
call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
});
|
||||
|
||||
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function must have a valid type");
|
||||
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
||||
// Adjust existing function type if it is used only by this function.
|
||||
old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}});
|
||||
} else {
|
||||
// Otherwise, either create a new type or use an existing one.
|
||||
std::vector<uint32_t> type_ids;
|
||||
type_ids.reserve(old_function_type->NumInOperands() + 1);
|
||||
|
||||
for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) {
|
||||
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
|
||||
}
|
||||
|
||||
type_ids.push_back(parameter_type_id);
|
||||
|
||||
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 TransformationAddParameter::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_parameter() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
62
source/fuzz/transformation_add_parameter.h
Normal file
62
source/fuzz/transformation_add_parameter.h
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_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 TransformationAddParameter : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddParameter(
|
||||
const protobufs::TransformationAddParameter& message);
|
||||
|
||||
TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id,
|
||||
uint32_t initializer_id,
|
||||
uint32_t function_type_fresh_id);
|
||||
|
||||
// - |function_id| must be a valid result id of some non-entry-point function
|
||||
// in the module.
|
||||
// - |initializer_id| must be a valid result id of some instruction in the
|
||||
// module. Instruction's type may not be OpTypeVoid.
|
||||
// - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
|
||||
// not equal.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Creates a new OpFunctionParameter instruction with result id
|
||||
// |parameter_fresh_id| for the function with |function_id|.
|
||||
// - Adjusts function's type to include a new parameter.
|
||||
// - Adds |initializer_id| as a new operand to every OpFunctionCall
|
||||
// instruction that calls the function.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddParameter message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
|
@ -1,201 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
|
||||
#include <source/spirv_constant.h>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddParameters::TransformationAddParameters(
|
||||
const protobufs::TransformationAddParameters& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddParameters::TransformationAddParameters(
|
||||
uint32_t function_id, uint32_t new_type_id,
|
||||
const std::vector<uint32_t>& new_parameter_type,
|
||||
const std::vector<uint32_t>& new_parameter_id,
|
||||
const std::vector<uint32_t>& constant_id) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_new_type_id(new_type_id);
|
||||
|
||||
for (auto id : new_parameter_type) {
|
||||
message_.add_new_parameter_type(id);
|
||||
}
|
||||
|
||||
for (auto id : new_parameter_id) {
|
||||
message_.add_new_parameter_id(id);
|
||||
}
|
||||
|
||||
for (auto id : constant_id) {
|
||||
message_.add_constant_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationAddParameters::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that function exists
|
||||
const auto* function =
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
if (!function ||
|
||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate new parameters.
|
||||
const auto& new_type_ids = message_.new_parameter_type();
|
||||
const auto& new_parameter_ids = message_.new_parameter_id();
|
||||
const auto& constant_ids = message_.constant_id();
|
||||
|
||||
// All three vectors must have the same size.
|
||||
if (new_type_ids.size() != new_parameter_ids.size() ||
|
||||
new_type_ids.size() != constant_ids.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vectors must have at least one component.
|
||||
if (new_type_ids.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that type ids exist in the module and are not OpTypeVoid.
|
||||
for (auto id : new_type_ids) {
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(id);
|
||||
if (!type || type->AsVoid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all parameter ids are fresh.
|
||||
for (auto id : new_parameter_ids) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that constants exist and have valid type.
|
||||
for (int i = 0, n = constant_ids.size(); i < n; ++i) {
|
||||
const auto* inst = ir_context->get_def_use_mgr()->GetDef(constant_ids[i]);
|
||||
if (!inst || inst->type_id() != new_type_ids[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate new function type.
|
||||
const auto* old_type_inst = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
const auto* new_type_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.new_type_id());
|
||||
|
||||
// Both types must exist.
|
||||
assert(old_type_inst && old_type_inst->opcode() == SpvOpTypeFunction);
|
||||
if (!new_type_inst || new_type_inst->opcode() != SpvOpTypeFunction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto num_old_parameters = old_type_inst->NumInOperands();
|
||||
auto num_new_parameters = new_type_ids.size();
|
||||
|
||||
// New function type has been added to the module which means that it's valid.
|
||||
// Thus, we don't need to check whether the limit on the number of arguments
|
||||
// is satisfied.
|
||||
|
||||
// New type = old type + new parameters.
|
||||
if (new_type_inst->NumInOperands() !=
|
||||
num_old_parameters + num_new_parameters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that old parameters and the return type are preserved.
|
||||
for (uint32_t i = 0; i < num_old_parameters; ++i) {
|
||||
if (new_type_inst->GetSingleWordInOperand(i) !=
|
||||
old_type_inst->GetSingleWordInOperand(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that new parameters have been appended.
|
||||
for (int i = 0; i < num_new_parameters; ++i) {
|
||||
if (new_type_inst->GetSingleWordInOperand(i + num_old_parameters) !=
|
||||
new_type_ids[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationAddParameters::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Retrieve all data from the message
|
||||
auto function_id = message_.function_id();
|
||||
const auto& new_parameter_type = message_.new_parameter_type();
|
||||
const auto& new_parameter_id = message_.new_parameter_id();
|
||||
const auto& constant_id = message_.constant_id();
|
||||
|
||||
// Find the function that will be transformed
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, function_id);
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
// Change function's type
|
||||
function->DefInst().SetInOperand(1, {message_.new_type_id()});
|
||||
|
||||
// Add new parameters to the function.
|
||||
for (int i = 0, n = new_parameter_id.size(); i < n; ++i) {
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFunctionParameter, new_parameter_type[i],
|
||||
new_parameter_id[i], opt::Instruction::OperandList()));
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
|
||||
}
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(),
|
||||
[function_id, &constant_id](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != function_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto id : constant_id) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// it would be good to mark this usage of |id| as irrelevant, so that
|
||||
// we can perform some interesting transformations on it later.
|
||||
call->AddOperand({SPV_OPERAND_TYPE_ID, {id}});
|
||||
}
|
||||
});
|
||||
|
||||
// Update module's id bound. We can safely dereference the result of
|
||||
// max_element since |new_parameter_id| is guaranteed to have elements.
|
||||
fuzzerutil::UpdateModuleIdBound(
|
||||
ir_context,
|
||||
*std::max_element(new_parameter_id.begin(), new_parameter_id.end()));
|
||||
|
||||
// Make sure our changes are analyzed.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddParameters::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_parameters() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
@ -1,70 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationAddParameters : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddParameters(
|
||||
const protobufs::TransformationAddParameters& message);
|
||||
|
||||
TransformationAddParameters(uint32_t function_id, uint32_t new_type_id,
|
||||
const std::vector<uint32_t>& new_parameter_type,
|
||||
const std::vector<uint32_t>& new_parameter_id,
|
||||
const std::vector<uint32_t>& constant_id);
|
||||
|
||||
// - |function_id| must be a valid result id of some non-entry-point function
|
||||
// in the module.
|
||||
// - |new_type_id| must be a result id of OpTypeFunction instruction.
|
||||
// - New type of the function must have the same return type. New function
|
||||
// parameters must be appended to the old ones.
|
||||
// - |new_parameter_type| contains result ids of some OpType* instructions in
|
||||
// the module. It may not contain result ids of OpTypeVoid.
|
||||
// - |new_parameter_id| contains fresh ids.
|
||||
// - |constant_id| contains result ids used to initialize new parameters. Type
|
||||
// ids of these instructions must be the same as |new_parameter_type| (i.e.
|
||||
// |new_parameter_type[i] == GetDef(constant_id[i])->type_id()|).
|
||||
// - |new_parameter_id|, |new_parameter_type| and |constant_id| should all
|
||||
// have the same size and may not be empty.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Creates new OpFunctionParameter instructions for the function with
|
||||
// |function_id|.
|
||||
// - Changes type of the function to |new_type_id|.
|
||||
// - Adds ids from |constant_id| to every OpFunctionCall instruction that
|
||||
// calls the function.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddParameters message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
|
@ -55,48 +55,19 @@ bool TransformationAddTypeFunction::IsApplicable(
|
||||
// exactly the same return and argument type ids. (Note that the type manager
|
||||
// does not allow us to check this, as it does not distinguish between
|
||||
// function types with different but isomorphic pointer argument types.)
|
||||
for (auto& inst : ir_context->module()->types_values()) {
|
||||
if (inst.opcode() != SpvOpTypeFunction) {
|
||||
// Consider only OpTypeFunction instructions.
|
||||
continue;
|
||||
}
|
||||
if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
|
||||
// Different return types - cannot be the same.
|
||||
continue;
|
||||
}
|
||||
if (inst.NumInOperands() !=
|
||||
1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
|
||||
// Different numbers of arguments - cannot be the same.
|
||||
continue;
|
||||
}
|
||||
bool found_argument_mismatch = false;
|
||||
for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
|
||||
if (message_.argument_type_id(index - 1) !=
|
||||
inst.GetSingleWordInOperand(index)) {
|
||||
// Argument mismatch - cannot be the same.
|
||||
found_argument_mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_argument_mismatch) {
|
||||
continue;
|
||||
}
|
||||
// Everything matches - the type is already declared.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
std::vector<uint32_t> type_ids = {message_.return_type_id()};
|
||||
type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
|
||||
message_.argument_type_id().end());
|
||||
return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0;
|
||||
}
|
||||
|
||||
void TransformationAddTypeFunction::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::Instruction::OperandList in_operands;
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
|
||||
for (auto argument_type_id : message_.argument_type_id()) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
|
||||
}
|
||||
ir_context->module()->AddType(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
std::vector<uint32_t> type_ids = {message_.return_type_id()};
|
||||
type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
|
||||
message_.argument_type_id().end());
|
||||
|
||||
fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids);
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
|
@ -12,10 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_permute_function_parameters.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_permute_function_parameters.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -28,10 +29,10 @@ TransformationPermuteFunctionParameters::
|
||||
|
||||
TransformationPermuteFunctionParameters::
|
||||
TransformationPermuteFunctionParameters(
|
||||
uint32_t function_id, uint32_t new_type_id,
|
||||
uint32_t function_id, uint32_t function_type_fresh_id,
|
||||
const std::vector<uint32_t>& permutation) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_new_type_id(new_type_id);
|
||||
message_.set_function_type_fresh_id(function_type_fresh_id);
|
||||
|
||||
for (auto index : permutation) {
|
||||
message_.add_permutation(index);
|
||||
@ -77,49 +78,46 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that new function's type is valid:
|
||||
// - Has the same number of operands
|
||||
// - Has the same result type as the old one
|
||||
// - Order of arguments is permuted
|
||||
auto new_type_id = message_.new_type_id();
|
||||
const auto* new_type = ir_context->get_def_use_mgr()->GetDef(new_type_id);
|
||||
|
||||
if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
|
||||
new_type->NumInOperands() != function_type->NumInOperands()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that both instructions have the same result type
|
||||
if (new_type->GetSingleWordInOperand(0) !=
|
||||
function_type->GetSingleWordInOperand(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that new function type has its arguments permuted
|
||||
for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
|
||||
// +1 to take return type into account
|
||||
if (new_type->GetSingleWordInOperand(i + 1) !=
|
||||
function_type->GetSingleWordInOperand(permutation[i] + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id());
|
||||
}
|
||||
|
||||
void TransformationPermuteFunctionParameters::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Retrieve all data from the message
|
||||
uint32_t function_id = message_.function_id();
|
||||
uint32_t new_type_id = message_.new_type_id();
|
||||
const auto& permutation = message_.permutation();
|
||||
|
||||
// Find the function that will be transformed
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, function_id);
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
auto* old_function_type_inst =
|
||||
fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type_inst && "Function must have a valid type");
|
||||
|
||||
// Change function's type
|
||||
function->DefInst().SetInOperand(1, {new_type_id});
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) {
|
||||
// If only the current function uses |old_function_type_inst| - change it
|
||||
// in-place.
|
||||
opt::Instruction::OperandList permuted_operands = {
|
||||
std::move(old_function_type_inst->GetInOperand(0))};
|
||||
for (auto index : message_.permutation()) {
|
||||
// +1 since the first operand to OpTypeFunction is a return type.
|
||||
permuted_operands.push_back(
|
||||
std::move(old_function_type_inst->GetInOperand(index + 1)));
|
||||
}
|
||||
|
||||
old_function_type_inst->SetInOperands(std::move(permuted_operands));
|
||||
} else {
|
||||
// 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(
|
||||
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||
ir_context, message_.function_type_fresh_id(), type_ids)});
|
||||
}
|
||||
|
||||
// Adjust OpFunctionParameter instructions
|
||||
|
||||
@ -133,7 +131,7 @@ void TransformationPermuteFunctionParameters::Apply(
|
||||
|
||||
// Permute parameters' ids and types
|
||||
std::vector<uint32_t> permuted_param_id, permuted_param_type;
|
||||
for (auto index : permutation) {
|
||||
for (auto index : message_.permutation()) {
|
||||
permuted_param_id.push_back(param_id[index]);
|
||||
permuted_param_type.push_back(param_type[index]);
|
||||
}
|
||||
@ -149,10 +147,9 @@ void TransformationPermuteFunctionParameters::Apply(
|
||||
|
||||
// Fix all OpFunctionCall instructions
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(),
|
||||
[function_id, &permutation](opt::Instruction* call) {
|
||||
&function->DefInst(), [this](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != function_id) {
|
||||
call->GetSingleWordInOperand(0) != message_.function_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -160,7 +157,7 @@ void TransformationPermuteFunctionParameters::Apply(
|
||||
call->GetInOperand(0) // Function id
|
||||
};
|
||||
|
||||
for (auto index : permutation) {
|
||||
for (auto index : message_.permutation()) {
|
||||
// Take function id into account
|
||||
call_operands.push_back(call->GetInOperand(index + 1));
|
||||
}
|
||||
|
@ -29,11 +29,11 @@ class TransformationPermuteFunctionParameters : public Transformation {
|
||||
const protobufs::TransformationPermuteFunctionParameters& message);
|
||||
|
||||
TransformationPermuteFunctionParameters(
|
||||
uint32_t function_id, uint32_t new_type_id,
|
||||
uint32_t function_id, uint32_t function_type_fresh_id,
|
||||
const std::vector<uint32_t>& permutation);
|
||||
|
||||
// - |function_id| is a valid non-entry-point OpFunction instruction
|
||||
// - |new_type_id| is a result id of a valid OpTypeFunction instruction.
|
||||
// - |function_type_fresh_id| is a fresh id.
|
||||
// New type is valid if:
|
||||
// - it has the same number of operands as the old one
|
||||
// - function's result type is the same as the old one
|
||||
@ -46,7 +46,7 @@ class TransformationPermuteFunctionParameters : public Transformation {
|
||||
|
||||
// - OpFunction instruction with |result_id == function_id| is changed.
|
||||
// Its arguments are permuted according to the |permutation| vector
|
||||
// - Changed function gets a new type specified by |type_id|
|
||||
// - Adjusts function's type to accommodate for permuted parameters.
|
||||
// - Calls to the function are adjusted accordingly
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
@ -37,7 +37,7 @@ if (${SPIRV_BUILD_FUZZER})
|
||||
transformation_add_global_variable_test.cpp
|
||||
transformation_add_local_variable_test.cpp
|
||||
transformation_add_no_contraction_decoration_test.cpp
|
||||
transformation_add_parameters_test.cpp
|
||||
transformation_add_parameter_test.cpp
|
||||
transformation_add_type_array_test.cpp
|
||||
transformation_add_type_boolean_test.cpp
|
||||
transformation_add_type_float_test.cpp
|
||||
|
126
test/fuzz/transformation_add_parameter_test.cpp
Normal file
126
test/fuzz/transformation_add_parameter_test.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationAddParameterTest, BasicTest) {
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
%2 = OpTypeVoid
|
||||
%7 = OpTypeBool
|
||||
%11 = OpTypeInt 32 1
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFunction %7 %7
|
||||
%8 = OpConstant %11 23
|
||||
%12 = OpConstantTrue %7
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpFunctionCall %7 %9 %12
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%9 = OpFunction %7 None %6
|
||||
%14 = OpFunctionParameter %7
|
||||
%10 = OpLabel
|
||||
OpReturnValue %12
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
validator_options);
|
||||
|
||||
// Can't modify entry point function.
|
||||
ASSERT_FALSE(TransformationAddParameter(4, 15, 12, 16)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// There is no function with result id 29.
|
||||
ASSERT_FALSE(TransformationAddParameter(29, 15, 8, 16)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Parameter id is not fresh.
|
||||
ASSERT_FALSE(TransformationAddParameter(9, 14, 8, 16)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Function type id is not fresh.
|
||||
ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 14)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Function type id and parameter type id are equal.
|
||||
ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 15)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Parameter's initializer doesn't exist.
|
||||
ASSERT_FALSE(TransformationAddParameter(9, 15, 15, 16)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Correct transformation.
|
||||
TransformationAddParameter correct(9, 15, 8, 16);
|
||||
ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
|
||||
correct.Apply(context.get(), &transformation_context);
|
||||
|
||||
// The module remains valid.
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
std::string expected_shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
%2 = OpTypeVoid
|
||||
%7 = OpTypeBool
|
||||
%11 = OpTypeInt 32 1
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFunction %7 %7 %11
|
||||
%8 = OpConstant %11 23
|
||||
%12 = OpConstantTrue %7
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpFunctionCall %7 %9 %12 %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%9 = OpFunction %7 None %6
|
||||
%14 = OpFunctionParameter %7
|
||||
%15 = OpFunctionParameter %11
|
||||
%10 = OpLabel
|
||||
OpReturnValue %12
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
@ -1,177 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationAddParametersTest, BasicTest) {
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
%2 = OpTypeVoid
|
||||
%7 = OpTypeBool
|
||||
%11 = OpTypeInt 32 1
|
||||
%3 = OpTypeFunction %2
|
||||
%20 = OpTypeFunction %2 %7
|
||||
%6 = OpTypeFunction %7 %7
|
||||
%12 = OpTypeFunction %7 %7 %11
|
||||
%13 = OpTypeFunction %7 %7 %7
|
||||
%14 = OpTypeFunction %11 %7 %11
|
||||
%15 = OpTypeFunction %7 %11 %11
|
||||
%16 = OpTypeFunction %7 %7 %11 %11
|
||||
%8 = OpConstant %11 23
|
||||
%17 = OpConstantTrue %7
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%18 = OpFunctionCall %7 %9 %17
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%9 = OpFunction %7 None %6
|
||||
%19 = OpFunctionParameter %7
|
||||
%10 = OpLabel
|
||||
OpReturnValue %17
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
FactManager fact_manager;
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
validator_options);
|
||||
|
||||
// Can't modify entry point function.
|
||||
ASSERT_FALSE(TransformationAddParameters(4, 6, {20}, {21}, {17})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// There is no function with result id 10.
|
||||
ASSERT_FALSE(TransformationAddParameters(29, 12, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// There is no OpTypeFunction instruction with result id 21.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 21, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Function type with id 6 has fewer parameters than required.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 6, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Function type with id 16 has more parameters than required.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 16, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// New function type is not OpTypeFunction instruction.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 11, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Return type is invalid.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 14, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Types of original parameters are invalid.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 15, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Types of new parameters are invalid.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 13, {11}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// OpTypeVoid can't be the type of function parameter.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {2}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Vectors, that describe parameters, have different sizes.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {21}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Vectors cannot be empty.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {}, {})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Parameters' ids are not fresh.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {20}, {8})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Constants for parameters don't exist.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {21})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Constants for parameters have invalid type.
|
||||
ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {17})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Correct transformation.
|
||||
TransformationAddParameters correct(9, 12, {11}, {21}, {8});
|
||||
ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
|
||||
correct.Apply(context.get(), &transformation_context);
|
||||
|
||||
// The module remains valid.
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
std::string expected_shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %4 "main"
|
||||
%2 = OpTypeVoid
|
||||
%7 = OpTypeBool
|
||||
%11 = OpTypeInt 32 1
|
||||
%3 = OpTypeFunction %2
|
||||
%20 = OpTypeFunction %2 %7
|
||||
%6 = OpTypeFunction %7 %7
|
||||
%12 = OpTypeFunction %7 %7 %11
|
||||
%13 = OpTypeFunction %7 %7 %7
|
||||
%14 = OpTypeFunction %11 %7 %11
|
||||
%15 = OpTypeFunction %7 %11 %11
|
||||
%16 = OpTypeFunction %7 %7 %11 %11
|
||||
%8 = OpConstant %11 23
|
||||
%17 = OpConstantTrue %7
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
%18 = OpFunctionCall %7 %9 %17 %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%9 = OpFunction %7 None %12
|
||||
%19 = OpFunctionParameter %7
|
||||
%21 = OpFunctionParameter %11
|
||||
%10 = OpLabel
|
||||
OpReturnValue %17
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_permute_function_parameters.h"
|
||||
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -73,7 +74,6 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
||||
%18 = OpTypeFunction %8 %7 %15 %17
|
||||
%24 = OpTypeBool
|
||||
%25 = OpTypeFunction %24 %15 %7
|
||||
%105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
|
||||
%31 = OpConstant %6 255
|
||||
%33 = OpConstant %6 0
|
||||
%34 = OpConstant %6 1
|
||||
@ -205,56 +205,46 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
||||
validator_options);
|
||||
|
||||
// Can't permute main function
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
|
||||
context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 105, {})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Can't permute invalid instruction
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 105, {})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Permutation has too many values
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {2, 1, 0, 3})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Permutation has too few values
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {0, 1})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Permutation has invalid values 1
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {3, 1, 0})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Permutation has invalid values 2
|
||||
ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 0, {2, 2, 1})
|
||||
ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 105, {2, 2, 1})
|
||||
.IsApplicable(context.get(), transformation_context),
|
||||
"Permutation has duplicates");
|
||||
#endif
|
||||
|
||||
// Type id is not an OpTypeFunction instruction
|
||||
// Result id for new function type is not fresh.
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Type id has incorrect number of operands
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// OpTypeFunction has operands out of order
|
||||
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Successful transformations
|
||||
{
|
||||
// Function has two operands of the same type:
|
||||
// initial OpTypeFunction should be enough
|
||||
TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
|
||||
TransformationPermuteFunctionParameters transformation(12, 105, {1, 0});
|
||||
ASSERT_TRUE(
|
||||
transformation.IsApplicable(context.get(), transformation_context));
|
||||
transformation.Apply(context.get(), &transformation_context);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
}
|
||||
{
|
||||
TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
|
||||
TransformationPermuteFunctionParameters transformation(28, 106, {1, 0});
|
||||
ASSERT_TRUE(
|
||||
transformation.IsApplicable(context.get(), transformation_context));
|
||||
transformation.Apply(context.get(), &transformation_context);
|
||||
@ -313,8 +303,7 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
||||
%17 = OpTypePointer Function %16
|
||||
%18 = OpTypeFunction %8 %7 %15 %17
|
||||
%24 = OpTypeBool
|
||||
%25 = OpTypeFunction %24 %15 %7
|
||||
%105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
|
||||
%25 = OpTypeFunction %24 %7 %15
|
||||
%31 = OpConstant %6 255
|
||||
%33 = OpConstant %6 0
|
||||
%34 = OpConstant %6 1
|
||||
@ -422,7 +411,7 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
|
||||
%55 = OpFunctionCall %8 %12 %54 %53
|
||||
OpReturnValue %55
|
||||
OpFunctionEnd
|
||||
%28 = OpFunction %24 None %105
|
||||
%28 = OpFunction %24 None %25
|
||||
%27 = OpFunctionParameter %7
|
||||
%26 = OpFunctionParameter %15
|
||||
%29 = OpLabel
|
||||
|
Loading…
Reference in New Issue
Block a user