spirv-fuzz: add FuzzerPassAddCompositeInserts (#3606)

Adds FuzzerPassAddCompositeInserts, which randomly adds new
OpCompositeInsert instructions. Each OpCompositeInsert instruction
yields a copy of an original composite with one subcomponent replaced
with an existing or newly added object. Synonym facts are added for the
unchanged components in the original and added composite, and for the
replaced subcomponent and the object, if possible.

Fixes #2859
This commit is contained in:
Antoni Karpiński 2020-08-19 12:56:03 +00:00 коммит произвёл GitHub
Родитель 582c276d43
Коммит a711c594b8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 1519 добавлений и 20 удалений

Просмотреть файл

@ -38,6 +38,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_context.h
fuzzer_pass.h
fuzzer_pass_add_access_chains.h
fuzzer_pass_add_composite_inserts.h
fuzzer_pass_add_composite_types.h
fuzzer_pass_add_copy_memory.h
fuzzer_pass_add_dead_blocks.h
@ -131,6 +132,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_adjust_branch_weights.h
transformation_composite_construct.h
transformation_composite_extract.h
transformation_composite_insert.h
transformation_compute_data_synonym_fact_closure.h
transformation_context.h
transformation_equation_instruction.h
@ -178,6 +180,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_context.cpp
fuzzer_pass.cpp
fuzzer_pass_add_access_chains.cpp
fuzzer_pass_add_composite_inserts.cpp
fuzzer_pass_add_composite_types.cpp
fuzzer_pass_add_copy_memory.cpp
fuzzer_pass_add_dead_blocks.cpp
@ -270,6 +273,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_adjust_branch_weights.cpp
transformation_composite_construct.cpp
transformation_composite_extract.cpp
transformation_composite_insert.cpp
transformation_compute_data_synonym_fact_closure.cpp
transformation_context.cpp
transformation_equation_instruction.cpp

Просмотреть файл

@ -21,6 +21,7 @@
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
@ -35,6 +36,7 @@
#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
#include "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
#include "source/fuzz/fuzzer_pass_add_stores.h"
#include "source/fuzz/fuzzer_pass_add_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
@ -60,7 +62,11 @@
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h"
#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
@ -210,6 +216,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddAccessChains>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddCompositeInserts>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddCompositeTypes>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
@ -249,6 +258,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddRelaxedDecorations>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
@ -300,6 +312,18 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

Просмотреть файл

@ -27,6 +27,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingCompositeInsert = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
@ -64,6 +65,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperToInsertInComposite = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
{50, 95};
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
@ -86,7 +89,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfPropagatingInstructionsUp = {20,
70};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 90};
kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
@ -153,6 +156,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
chance_of_adding_array_or_struct_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
chance_of_adding_composite_insert_ =
ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert);
chance_of_adding_copy_memory_ =
ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory);
chance_of_adding_dead_block_ =
@ -207,6 +212,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
chance_of_donating_additional_module_ =
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_going_deeper_to_insert_in_composite_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite);
chance_of_going_deeper_when_making_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
chance_of_interchanging_signedness_of_integer_operands_ =

Просмотреть файл

@ -115,6 +115,9 @@ class FuzzerContext {
uint32_t GetChanceOfAddingArrayOrStructType() {
return chance_of_adding_array_or_struct_type_;
}
uint32_t GetChanceOfAddingCompositeInsert() {
return chance_of_adding_composite_insert_;
}
uint32_t GetChanceOfAddingCopyMemory() {
return chance_of_adding_copy_memory_;
}
@ -186,6 +189,9 @@ class FuzzerContext {
uint32_t GetChanceOfDonatingAdditionalModule() {
return chance_of_donating_additional_module_;
}
uint32_t GetChanceOfGoingDeeperToInsertInComposite() {
return chance_of_going_deeper_to_insert_in_composite_;
}
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
return chance_of_going_deeper_when_making_access_chain_;
}
@ -296,6 +302,9 @@ class FuzzerContext {
uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
return random_generator_->RandomUint32(composite_size_bound);
}
uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) {
return random_generator_->RandomUint32(number_of_components);
}
uint32_t GetRandomLoopControlPartialCount() {
return random_generator_->RandomUint32(max_loop_control_partial_count_);
}
@ -342,6 +351,7 @@ class FuzzerContext {
uint32_t chance_of_adding_access_chain_;
uint32_t chance_of_adding_another_struct_field_;
uint32_t chance_of_adding_array_or_struct_type_;
uint32_t chance_of_adding_composite_insert_;
uint32_t chance_of_adding_copy_memory_;
uint32_t chance_of_adding_dead_block_;
uint32_t chance_of_adding_dead_break_;
@ -371,6 +381,7 @@ class FuzzerContext {
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_going_deeper_to_insert_in_composite_;
uint32_t chance_of_going_deeper_when_making_access_chain_;
uint32_t chance_of_interchanging_signedness_of_integer_operands_;
uint32_t chance_of_interchanging_zero_like_constants_;

Просмотреть файл

@ -518,6 +518,26 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
}
}
bool FuzzerPass::CanFindOrCreateZeroConstant(const opt::analysis::Type& type) {
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(),
[this](const opt::analysis::Type* element_type) {
return CanFindOrCreateZeroConstant(*element_type);
});
default:
return false;
}
}
void FuzzerPass::MaybeAddUseToReplace(
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*

Просмотреть файл

@ -273,6 +273,9 @@ class FuzzerPass {
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
bool is_irrelevant);
// Checks if FindOrCreateZeroConstant can be called on this type.
bool CanFindOrCreateZeroConstant(const opt::analysis::Type& type);
// Adds a pair (id_use_descriptor, |replacement_id|) to the vector
// |uses_to_replace|, where id_use_descriptor is the id use descriptor
// representing the usage of an id in the |use_inst| instruction, at operand

Просмотреть файл

@ -0,0 +1,236 @@
// Copyright (c) 2020 Google LLC
//
// 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_add_composite_inserts.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/fuzz/transformation_composite_insert.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassAddCompositeInserts::~FuzzerPassAddCompositeInserts() = default;
void FuzzerPassAddCompositeInserts::Apply() {
ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator instruction_iterator,
const protobufs::InstructionDescriptor& instruction_descriptor)
-> void {
assert(instruction_iterator->opcode() ==
instruction_descriptor.target_instruction_opcode() &&
"The opcode of the instruction we might insert before must be "
"the same as the opcode in the descriptor for the instruction");
// Randomly decide whether to try adding an OpCompositeInsert
// instruction.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) {
return;
}
// It must be possible to insert an OpCompositeInsert instruction
// before |instruction_iterator|.
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
SpvOpCompositeInsert, instruction_iterator)) {
return;
}
// Look for available values that have composite type.
std::vector<opt::Instruction*> available_composites =
FindAvailableInstructions(
function, block, instruction_iterator,
[instruction_descriptor](
opt::IRContext* ir_context,
opt::Instruction* instruction) -> bool {
// |instruction| must be a supported instruction of composite
// type.
if (!TransformationCompositeInsert::
IsCompositeInstructionSupported(ir_context,
instruction)) {
return false;
}
auto instruction_type = ir_context->get_type_mgr()->GetType(
instruction->type_id());
// No components of the composite can have type
// OpTypeRuntimeArray.
if (ContainsRuntimeArray(*instruction_type)) {
return false;
}
// No components of the composite can be pointers.
// TODO:
// (https://github.com/KhronosGroup/SPIRV-Tools/issues/3658)
// Structs can have components of pointer type.
// FindOrCreateZeroConstant cannot be called on a
// pointer. We ignore pointers for now. Consider adding
// support for pointer types.
if (ContainsPointer(*instruction_type)) {
return false;
}
return true;
});
// If there are no available values, then return.
if (available_composites.empty()) {
return;
}
// Choose randomly one available composite value.
auto available_composite =
available_composites[GetFuzzerContext()->RandomIndex(
available_composites)];
// Take a random component of the chosen composite value. If the chosen
// component is itself a composite, then randomly decide whether to take
// its component and repeat.
uint32_t current_node_type_id = available_composite->type_id();
std::vector<uint32_t> path_to_replaced;
while (true) {
auto current_node_type_inst =
GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id);
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
*current_node_type_inst, GetIRContext());
// If the composite is empty, then end the iteration.
if (num_of_components == 0) {
break;
}
uint32_t one_selected_index =
GetFuzzerContext()->GetRandomIndexForCompositeInsert(
num_of_components);
// Construct a final index by appending the current index.
path_to_replaced.push_back(one_selected_index);
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
GetIRContext(), current_node_type_id, one_selected_index);
// If the component is not a composite then end the iteration.
if (!fuzzerutil::IsCompositeType(
GetIRContext()->get_type_mgr()->GetType(
current_node_type_id))) {
break;
}
// If the component is a composite, but we decide not to go deeper,
// then end the iteration.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfGoingDeeperToInsertInComposite())) {
break;
}
}
// Look for available objects that have the type id
// |current_node_type_id| and can be inserted.
std::vector<opt::Instruction*> available_objects =
FindAvailableInstructions(
function, block, instruction_iterator,
[instruction_descriptor, current_node_type_id](
opt::IRContext* /*unused*/,
opt::Instruction* instruction) -> bool {
if (instruction->result_id() == 0 ||
instruction->type_id() == 0) {
return false;
}
if (instruction->type_id() != current_node_type_id) {
return false;
}
return true;
});
// If there are no objects of the specific type available, check if
// FindOrCreateZeroConstant can be called and create a zero constant of
// this type.
uint32_t available_object_id;
if (available_objects.empty()) {
auto current_node_type =
GetIRContext()->get_type_mgr()->GetType(current_node_type_id);
if (!CanFindOrCreateZeroConstant(*current_node_type)) {
return;
}
available_object_id =
FindOrCreateZeroConstant(current_node_type_id, false);
} else {
available_object_id =
available_objects[GetFuzzerContext()->RandomIndex(
available_objects)]
->result_id();
}
auto new_result_id = GetFuzzerContext()->GetFreshId();
// Insert an OpCompositeInsert instruction which copies
// |available_composite| and in the copy inserts the object
// of type |available_object_id| at index |index_to_replace|.
ApplyTransformation(TransformationCompositeInsert(
instruction_descriptor, new_result_id,
available_composite->result_id(), available_object_id,
path_to_replaced));
});
}
bool FuzzerPassAddCompositeInserts::ContainsPointer(
const opt::analysis::Type& type) {
switch (type.kind()) {
case opt::analysis::Type::kPointer:
return true;
case opt::analysis::Type::kArray:
return ContainsPointer(*type.AsArray()->element_type());
case opt::analysis::Type::kMatrix:
return ContainsPointer(*type.AsMatrix()->element_type());
case opt::analysis::Type::kVector:
return ContainsPointer(*type.AsVector()->element_type());
case opt::analysis::Type::kStruct:
return std::any_of(type.AsStruct()->element_types().begin(),
type.AsStruct()->element_types().end(),
[](const opt::analysis::Type* element_type) {
return ContainsPointer(*element_type);
});
default:
return false;
}
}
bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray(
const opt::analysis::Type& type) {
switch (type.kind()) {
case opt::analysis::Type::kRuntimeArray:
return true;
case opt::analysis::Type::kStruct:
// If any component of a struct is of type OpTypeRuntimeArray, return
// true.
return std::any_of(type.AsStruct()->element_types().begin(),
type.AsStruct()->element_types().end(),
[](const opt::analysis::Type* element_type) {
return ContainsRuntimeArray(*element_type);
});
default:
return false;
}
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright (c) 2020 Google LLC
//
// 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 SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H
#define SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Fuzzer pass that randomly adds new OpCompositeInsert instructions to
// available values that have the composite type.
class FuzzerPassAddCompositeInserts : public FuzzerPass {
public:
FuzzerPassAddCompositeInserts(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddCompositeInserts();
void Apply() override;
// Checks if any component of a composite is a pointer.
static bool ContainsPointer(const opt::analysis::Type& type);
// Checks if any component of a composite has type OpTypeRuntimeArray.
static bool ContainsRuntimeArray(const opt::analysis::Type& type);
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H

Просмотреть файл

@ -37,6 +37,7 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
std::vector<opt::Instruction> instructions_for_transformation;
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
for (auto& instruction : block) {
@ -52,25 +53,27 @@ void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
IsInstructionSuitable(GetIRContext(), instruction)) {
continue;
}
// Get the operand type id. We know that both operands have the same
// type.
uint32_t operand_type_id =
GetIRContext()
->get_def_use_mgr()
->GetDef(instruction.GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
// Ensure the required struct type exists. The struct type is based on
// the operand type.
FindOrCreateStructType({operand_type_id, operand_type_id});
ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended(
GetFuzzerContext()->GetFreshId(), instruction.result_id()));
instructions_for_transformation.push_back(instruction);
}
}
}
for (auto& instruction : instructions_for_transformation) {
// Get the operand type id. We know that both operands have the same
// type.
uint32_t operand_type_id =
GetIRContext()
->get_def_use_mgr()
->GetDef(instruction.GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
// Ensure the required struct type exists. The struct type is based on
// the operand type.
FindOrCreateStructType({operand_type_id, operand_type_id});
ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended(
GetFuzzerContext()->GetFreshId(), instruction.result_id()));
}
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -66,7 +66,14 @@ void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
? SpvStorageClassPrivate
: SpvStorageClassFunction;
// Find or create a constant to initialize the variable from.
// Find or create a constant to initialize the variable from. The type of
// |instruction| must be such that the function FindOrCreateConstant can be
// called.
auto instruction_type =
GetIRContext()->get_type_mgr()->GetType(instruction->type_id());
if (!CanFindOrCreateZeroConstant(*instruction_type)) {
return;
}
auto variable_initializer_id =
FindOrCreateZeroConstant(instruction->type_id(), false);

Просмотреть файл

@ -1353,6 +1353,5 @@ opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
}
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -498,7 +498,6 @@ opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
SpvOp opcode);
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -412,6 +412,7 @@ message Transformation {
TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
TransformationPropagateInstructionUp propagate_instruction_up = 67;
TransformationCompositeInsert composite_insert = 68;
// Add additional option using the next available number.
}
}
@ -1021,6 +1022,29 @@ message TransformationCompositeExtract {
}
message TransformationCompositeInsert {
// A transformation that adds an instruction OpCompositeInsert which creates
// a new composite from an existing composite, with an element inserted.
// A descriptor for an instruction before which the new instruction
// OpCompositeInsert should be inserted.
InstructionDescriptor instruction_to_insert_before = 1;
// Result id of the inserted OpCompositeInsert instruction.
uint32 fresh_id = 2;
// Id of the composite used as the basis for the insertion.
uint32 composite_id = 3;
// Id of the object to be inserted.
uint32 object_id = 4;
// Indices that indicate which part of the composite should be inserted into.
repeated uint32 index = 5;
}
message TransformationComputeDataSynonymFactClosure {
// A transformation that impacts the fact manager only, forcing a computation

Просмотреть файл

@ -49,6 +49,7 @@
#include "source/fuzz/transformation_adjust_branch_weights.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_composite_insert.h"
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
#include "source/fuzz/transformation_equation_instruction.h"
#include "source/fuzz/transformation_function_call.h"
@ -179,6 +180,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kCompositeExtract:
return MakeUnique<TransformationCompositeExtract>(
message.composite_extract());
case protobufs::Transformation::TransformationCase::kCompositeInsert:
return MakeUnique<TransformationCompositeInsert>(
message.composite_insert());
case protobufs::Transformation::TransformationCase::
kComputeDataSynonymFactClosure:
return MakeUnique<TransformationComputeDataSynonymFactClosure>(

Просмотреть файл

@ -0,0 +1,226 @@
// Copyright (c) 2020 Google LLC
//
// 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 "transformation_composite_insert.h"
#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationCompositeInsert::TransformationCompositeInsert(
const spvtools::fuzz::protobufs::TransformationCompositeInsert& message)
: message_(message) {}
TransformationCompositeInsert::TransformationCompositeInsert(
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id, uint32_t composite_id, uint32_t object_id,
const std::vector<uint32_t>& index) {
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
message_.set_fresh_id(fresh_id);
message_.set_composite_id(composite_id);
message_.set_object_id(object_id);
for (auto an_index : index) {
message_.add_index(an_index);
}
}
bool TransformationCompositeInsert::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// |message_.fresh_id| must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
// |message_.composite_id| must refer to an existing composite value.
auto composite =
ir_context->get_def_use_mgr()->GetDef(message_.composite_id());
if (!IsCompositeInstructionSupported(ir_context, composite)) {
return false;
}
// The indices in |message_.index| must be suitable for indexing into
// |composite->type_id()|.
auto component_to_be_replaced_type_id = fuzzerutil::WalkCompositeTypeIndices(
ir_context, composite->type_id(), message_.index());
if (component_to_be_replaced_type_id == 0) {
return false;
}
// The instruction having the id of |message_.object_id| must be defined.
auto object_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.object_id());
if (object_instruction == nullptr || object_instruction->type_id() == 0) {
return false;
}
// We ignore pointers for now.
auto object_instruction_type =
ir_context->get_type_mgr()->GetType(object_instruction->type_id());
if (object_instruction_type->AsPointer() != nullptr) {
return false;
}
// The type id of the object having |message_.object_id| and the type id of
// the component of the composite at index |message_.index| must be the same.
if (component_to_be_replaced_type_id != object_instruction->type_id()) {
return false;
}
// |message_.instruction_to_insert_before| must be a defined instruction.
auto instruction_to_insert_before =
FindInstruction(message_.instruction_to_insert_before(), ir_context);
if (instruction_to_insert_before == nullptr) {
return false;
}
// |message_.composite_id| and |message_.object_id| must be available before
// the |message_.instruction_to_insert_before|.
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, instruction_to_insert_before, message_.composite_id())) {
return false;
}
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, instruction_to_insert_before, message_.object_id())) {
return false;
}
// It must be possible to insert an OpCompositeInsert before this
// instruction.
return fuzzerutil::CanInsertOpcodeBeforeInstruction(
SpvOpCompositeInsert, instruction_to_insert_before);
}
void TransformationCompositeInsert::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// |message_.struct_fresh_id| must be fresh.
assert(fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) &&
"|message_.fresh_id| must be fresh");
std::vector<uint32_t> index =
fuzzerutil::RepeatedFieldToVector(message_.index());
opt::Instruction::OperandList in_operands;
in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.object_id()}});
in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}});
for (auto i : index) {
in_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}});
}
auto composite_type_id =
fuzzerutil::GetTypeId(ir_context, message_.composite_id());
FindInstruction(message_.instruction_to_insert_before(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeInsert, composite_type_id,
message_.fresh_id(), std::move(in_operands)));
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
// We have modified the module so most analyzes are now invalid.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// Add facts about synonyms. Every element which hasn't been changed in
// the copy is synonymous to the corresponding element in the original
// composite which has id |message_.composite_id|. For every index that is a
// prefix of |index|, the components different from the one that
// contains the inserted object are synonymous with corresponding
// elements in the original composite.
// If |composite_id| is irrelevant then don't add any synonyms.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.composite_id())) {
return;
}
uint32_t current_node_type_id = composite_type_id;
std::vector<uint32_t> current_index;
for (uint32_t current_level = 0; current_level < index.size();
current_level++) {
auto current_node_type_inst =
ir_context->get_def_use_mgr()->GetDef(current_node_type_id);
uint32_t index_to_skip = index[current_level];
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
*current_node_type_inst, ir_context);
// Update the current_node_type_id.
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, current_node_type_id, index_to_skip);
for (uint32_t i = 0; i < num_of_components; i++) {
if (i == index_to_skip) {
continue;
}
current_index.push_back(i);
// TODO: (https://github.com/KhronosGroup/SPIRV-Tools/issues/3659)
// Google C++ guide restricts the use of r-value references.
// https://google.github.io/styleguide/cppguide.html#Rvalue_references
// Consider changing the signature of MakeDataDescriptor()
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(),
std::vector<uint32_t>(current_index)),
MakeDataDescriptor(message_.composite_id(),
std::vector<uint32_t>(current_index)),
ir_context);
current_index.pop_back();
}
// Store the prefix of the |index|.
current_index.push_back(index[current_level]);
}
// The element which has been changed is synonymous to the found object
// itself. Add this fact only if |object_id| is not irrelevant.
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.object_id())) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.object_id(), {}),
MakeDataDescriptor(message_.fresh_id(), std::vector<uint32_t>(index)),
ir_context);
}
}
protobufs::Transformation TransformationCompositeInsert::ToMessage() const {
protobufs::Transformation result;
*result.mutable_composite_insert() = message_;
return result;
}
bool TransformationCompositeInsert::IsCompositeInstructionSupported(
opt::IRContext* ir_context, opt::Instruction* instruction) {
if (instruction == nullptr) {
return false;
}
if (instruction->result_id() == 0 || instruction->type_id() == 0) {
return false;
}
auto composite_type =
ir_context->get_type_mgr()->GetType(instruction->type_id());
if (!fuzzerutil::IsCompositeType(composite_type)) {
return false;
}
// Empty composites are not supported.
auto instruction_type_inst =
ir_context->get_def_use_mgr()->GetDef(instruction->type_id());
if (fuzzerutil::GetBoundForCompositeIndex(*instruction_type_inst,
ir_context) == 0) {
return false;
}
return true;
}
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -0,0 +1,72 @@
// Copyright (c) 2020 Google LLC
//
// 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 SPIRV_TOOLS_TRANSFORMATION_COMPOSITE_INSERT_H
#define SPIRV_TOOLS_TRANSFORMATION_COMPOSITE_INSERT_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 TransformationCompositeInsert : public Transformation {
public:
explicit TransformationCompositeInsert(
const protobufs::TransformationCompositeInsert& message);
TransformationCompositeInsert(
const protobufs::InstructionDescriptor& instruction_to_insert_before,
uint32_t fresh_id, uint32_t composite_id, uint32_t object_id,
const std::vector<uint32_t>& index);
// - |message_.fresh_id| must be fresh.
// - |message_.composite_id| must refer to an existing composite value.
// - |message_.index| must refer to a correct index in the composite.
// - The type id of the object and the type id of the component of the
// composite at index |message_.index| must be the same.
// - |message_.instruction_to_insert_before| must refer to a defined
// instruction.
// - It must be possible to insert OpCompositeInsert before
// |instruction_to_insert_before|.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Adds an instruction OpCompositeInsert before
// |instruction_to_insert_before|, which creates a new composite from
// |composite_id| by inserting |object_id| at the specified |index|.
// Synonyms are created between those components which are identical in the
// original and the modified composite and between the inserted object and its
// copy in the modified composite.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
// Checks if |instruction| is a instruction of a composite type supported by
// this transformation.
static bool IsCompositeInstructionSupported(opt::IRContext* ir_context,
opt::Instruction* instruction);
private:
protobufs::TransformationCompositeInsert message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_TRANSFORMATION_COMPOSITE_INSERT_H

Просмотреть файл

@ -57,6 +57,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_adjust_branch_weights_test.cpp
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_composite_insert_test.cpp
transformation_compute_data_synonym_fact_closure_test.cpp
transformation_equation_instruction_test.cpp
transformation_function_call_test.cpp

Просмотреть файл

@ -0,0 +1,814 @@
// Copyright (c) 2020 Google LLC
//
// 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_composite_insert.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationCompositeInsertTest, NotApplicableScenarios) {
// This test handles cases where IsApplicable() returns false.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
OpName %18 "level_1"
OpMemberName %18 0 "b1"
OpMemberName %18 1 "b2"
OpName %20 "l1"
OpName %24 "level_2"
OpMemberName %24 0 "c1"
OpMemberName %24 1 "c2"
OpName %26 "l2"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%18 = OpTypeStruct %12 %12
%19 = OpTypePointer Function %18
%24 = OpTypeStruct %18 %18
%25 = OpTypePointer Function %24
%30 = OpTypeBool
%31 = OpConstantTrue %30
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%20 = OpVariable %19 Function
%26 = OpVariable %25 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%21 = OpLoad %12 %14
%22 = OpLoad %12 %14
%23 = OpCompositeConstruct %18 %21 %22
OpStore %20 %23
%27 = OpLoad %18 %20
%28 = OpLoad %18 %20
%29 = OpCompositeConstruct %24 %27 %28
OpStore %26 %29
OpSelectionMerge %33 None
OpBranchConditional %31 %32 %33
%32 = OpLabel
OpBranch %33
%33 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: |fresh_id| is not fresh.
auto transformation_bad_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 20, 29, 11, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: |composite_id| does not refer to a existing instruction.
auto transformation_bad_2 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 40, 11, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: |composite_id| does not refer to a composite value.
auto transformation_bad_3 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 9, 11, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: |object_id| does not refer to a defined instruction.
auto transformation_bad_4 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 40, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
// Bad: |object_id| cannot refer to a pointer.
auto transformation_bad_5 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 8, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_5.IsApplicable(context.get(), transformation_context));
// Bad: |index| is not a correct index.
auto transformation_bad_6 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {2, 0, 0});
ASSERT_FALSE(
transformation_bad_6.IsApplicable(context.get(), transformation_context));
// Bad: Type id of the object to be inserted and the type id of the
// component at |index| are not the same.
auto transformation_bad_7 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {1, 0});
ASSERT_FALSE(
transformation_bad_7.IsApplicable(context.get(), transformation_context));
// Bad: |instruction_to_insert_before| does not refer to a defined
// instruction.
auto transformation_bad_8 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpIMul, 0), 50, 29, 11, {1, 0, 0});
ASSERT_FALSE(
transformation_bad_8.IsApplicable(context.get(), transformation_context));
// Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with
// OpSelectionMerge above it.
auto transformation_bad_9 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpBranchConditional, 0), 50, 29, 11,
{1, 0, 0});
ASSERT_FALSE(
transformation_bad_9.IsApplicable(context.get(), transformation_context));
// Bad: |composite_id| does not have a type_id.
auto transformation_bad_10 = TransformationCompositeInsert(
MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 1, 11, {1, 0, 0});
ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(),
transformation_context));
}
TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) {
// This test handles cases where either the composite is empty or the
// composite contains an empty composite.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
%2 = OpTypeVoid
%60 = OpTypeStruct
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%61 = OpConstantComposite %60
%62 = OpConstantComposite %60
%12 = OpTypeStruct %6 %6
%63 = OpTypeStruct %6 %60
%13 = OpTypePointer Function %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
%64 = OpCompositeConstruct %63 %15 %61
OpStore %14 %17
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: The composite with |composite_id| cannot be empty.
auto transformation_bad_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 61, 62, {1});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Good: It is possible to insert into a composite an element which is an
// empty composite.
auto transformation_good_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 64, 62, {1});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
transformation_good_1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformations = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
%2 = OpTypeVoid
%60 = OpTypeStruct
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%61 = OpConstantComposite %60
%62 = OpConstantComposite %60
%12 = OpTypeStruct %6 %6
%63 = OpTypeStruct %6 %60
%13 = OpTypePointer Function %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
%64 = OpCompositeConstruct %63 %15 %61
%50 = OpCompositeInsert %63 %62 %64 1
OpStore %14 %17
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) {
// This test handles cases where either |composite| is irrelevant.
// The transformation shouldn't create any synonyms.
// The member composite has a different number of elements than the parent
// composite.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
OpName %18 "level_1"
OpMemberName %18 0 "b1"
OpMemberName %18 1 "b2"
OpMemberName %18 2 "b3"
OpName %20 "l1"
OpName %25 "level_2"
OpMemberName %25 0 "c1"
OpMemberName %25 1 "c2"
OpName %27 "l2"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%18 = OpTypeStruct %12 %12 %12
%19 = OpTypePointer Function %18
%25 = OpTypeStruct %18 %18
%26 = OpTypePointer Function %25
%31 = OpTypeBool
%32 = OpConstantTrue %31
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%20 = OpVariable %19 Function
%27 = OpVariable %26 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%21 = OpLoad %12 %14
%22 = OpLoad %12 %14
%23 = OpLoad %12 %14
%24 = OpCompositeConstruct %18 %21 %22 %23
OpStore %20 %24
%28 = OpLoad %18 %20
%29 = OpLoad %18 %20
%30 = OpCompositeConstruct %25 %28 %29
OpStore %27 %30
OpSelectionMerge %34 None
OpBranchConditional %32 %33 %34
%33 = OpLabel
OpBranch %34
%34 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Add fact that the composite is irrelevant.
fact_manager.AddFactIdIsIrrelevant(30);
auto transformation_good_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
transformation_good_1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// No synonyms should have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1, 0, 0}),
MakeDataDescriptor(11, {})));
}
TEST(TransformationCompositeInsertTest, IrrelevantObjectSomeSynonyms) {
// This test handles cases where |object| is irrelevant.
// The transformation should create some synonyms. It shouldn't create a
// synonym related to |object|. The member composite has a different number of
// elements than the parent composite.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
OpName %18 "level_1"
OpMemberName %18 0 "b1"
OpMemberName %18 1 "b2"
OpMemberName %18 2 "b3"
OpName %20 "l1"
OpName %25 "level_2"
OpMemberName %25 0 "c1"
OpMemberName %25 1 "c2"
OpName %27 "l2"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%18 = OpTypeStruct %12 %12 %12
%19 = OpTypePointer Function %18
%25 = OpTypeStruct %18 %18
%26 = OpTypePointer Function %25
%31 = OpTypeBool
%32 = OpConstantTrue %31
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%20 = OpVariable %19 Function
%27 = OpVariable %26 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%21 = OpLoad %12 %14
%22 = OpLoad %12 %14
%23 = OpLoad %12 %14
%24 = OpCompositeConstruct %18 %21 %22 %23
OpStore %20 %24
%28 = OpLoad %18 %20
%29 = OpLoad %18 %20
%30 = OpCompositeConstruct %25 %28 %29
OpStore %27 %30
OpSelectionMerge %34 None
OpBranchConditional %32 %33 %34
%33 = OpLabel
OpBranch %34
%34 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Add fact that the object is irrelevant.
fact_manager.AddFactIdIsIrrelevant(11);
auto transformation_good_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
transformation_good_1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// These synonyms should have been added.
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
// This synonym shouldn't have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1, 0, 0}),
MakeDataDescriptor(11, {})));
}
TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) {
// This test handles cases where neither |composite| nor |object| is
// irrelevant. The transformation should create synonyms.
// The member composite has a different number of elements than the parent
// composite.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
OpName %18 "level_1"
OpMemberName %18 0 "b1"
OpMemberName %18 1 "b2"
OpMemberName %18 2 "b3"
OpName %20 "l1"
OpName %25 "level_2"
OpMemberName %25 0 "c1"
OpMemberName %25 1 "c2"
OpName %27 "l2"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%18 = OpTypeStruct %12 %12 %12
%19 = OpTypePointer Function %18
%25 = OpTypeStruct %18 %18
%26 = OpTypePointer Function %25
%31 = OpTypeBool
%32 = OpConstantTrue %31
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%20 = OpVariable %19 Function
%27 = OpVariable %26 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%21 = OpLoad %12 %14
%22 = OpLoad %12 %14
%23 = OpLoad %12 %14
%24 = OpCompositeConstruct %18 %21 %22 %23
OpStore %20 %24
%28 = OpLoad %18 %20
%29 = OpLoad %18 %20
%30 = OpCompositeConstruct %25 %28 %29
OpStore %27 %30
OpSelectionMerge %34 None
OpBranchConditional %32 %33 %34
%33 = OpLabel
OpBranch %34
%34 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
auto transformation_good_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0});
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
transformation_good_1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// These synonyms should have been added.
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1, 0, 0}),
MakeDataDescriptor(11, {})));
// These synonyms should not have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
MakeDataDescriptor(50, {1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0}),
MakeDataDescriptor(50, {1, 0})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 0}),
MakeDataDescriptor(50, {1, 0, 0})));
auto transformation_good_2 = TransformationCompositeInsert(
MakeInstructionDescriptor(50, SpvOpStore, 0), 51, 50, 11, {0, 1, 1});
ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
transformation_context));
transformation_good_2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// These synonyms should have been added.
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1}),
MakeDataDescriptor(51, {1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 0}),
MakeDataDescriptor(51, {0, 0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 2}),
MakeDataDescriptor(51, {0, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 1, 0}),
MakeDataDescriptor(51, {0, 1, 0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(51, {0, 1, 1}),
MakeDataDescriptor(11, {})));
// These synonyms should not have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0}),
MakeDataDescriptor(51, {0})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 1}),
MakeDataDescriptor(51, {0, 1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 1, 1}),
MakeDataDescriptor(51, {0, 1, 1})));
std::string after_transformations = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b"
OpName %18 "level_1"
OpMemberName %18 0 "b1"
OpMemberName %18 1 "b2"
OpMemberName %18 2 "b3"
OpName %20 "l1"
OpName %25 "level_2"
OpMemberName %25 0 "c1"
OpMemberName %25 1 "c2"
OpName %27 "l2"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%18 = OpTypeStruct %12 %12 %12
%19 = OpTypePointer Function %18
%25 = OpTypeStruct %18 %18
%26 = OpTypePointer Function %25
%31 = OpTypeBool
%32 = OpConstantTrue %31
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%20 = OpVariable %19 Function
%27 = OpVariable %26 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%21 = OpLoad %12 %14
%22 = OpLoad %12 %14
%23 = OpLoad %12 %14
%24 = OpCompositeConstruct %18 %21 %22 %23
OpStore %20 %24
%28 = OpLoad %18 %20
%29 = OpLoad %18 %20
%30 = OpCompositeConstruct %25 %28 %29
%50 = OpCompositeInsert %25 %11 %30 1 0 0
%51 = OpCompositeInsert %25 %11 %50 0 1 1
OpStore %27 %30
OpSelectionMerge %34 None
OpBranchConditional %32 %33 %34
%33 = OpLabel
OpBranch %34
%34 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) {
// This test handles cases where either the composite or the object is not
// available before the |instruction_to_insert_before|.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "base"
OpMemberName %12 0 "a1"
OpMemberName %12 1 "a2"
OpName %14 "b1"
OpName %18 "b2"
OpName %22 "lvl1"
OpMemberName %22 0 "b1"
OpMemberName %22 1 "b2"
OpName %24 "l1"
OpName %28 "i3"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeStruct %6 %6
%13 = OpTypePointer Function %12
%22 = OpTypeStruct %12 %12
%23 = OpTypePointer Function %22
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%18 = OpVariable %13 Function
%24 = OpVariable %23 Function
%28 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%15 = OpLoad %6 %8
%16 = OpLoad %6 %10
%17 = OpCompositeConstruct %12 %15 %16
OpStore %14 %17
%19 = OpLoad %6 %10
%20 = OpLoad %6 %8
%21 = OpCompositeConstruct %12 %19 %20
OpStore %18 %21
%25 = OpLoad %12 %14
%26 = OpLoad %12 %18
%27 = OpCompositeConstruct %22 %25 %26
OpStore %24 %27
%29 = OpLoad %6 %8
%30 = OpLoad %6 %10
%31 = OpIMul %6 %29 %30
OpStore %28 %31
%60 = OpCompositeConstruct %12 %20 %19
%61 = OpCompositeConstruct %22 %26 %25
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
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);
// Bad: The object with |object_id| is not available at
// |instruction_to_insert_before|.
auto transformation_bad_1 = TransformationCompositeInsert(
MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 27, 60, {1});
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: The composite with |composite_id| is not available at
// |instruction_to_insert_before|.
auto transformation_bad_2 = TransformationCompositeInsert(
MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 61, 21, {1});
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The |instruction_to_insert_before| is the composite itself and is
// available.
auto transformation_bad_3 = TransformationCompositeInsert(
MakeInstructionDescriptor(61, SpvOpCompositeConstruct, 0), 50, 61, 21,
{1});
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: The |instruction_to_insert_before| is the object itself and is not
// available.
auto transformation_bad_4 = TransformationCompositeInsert(
MakeInstructionDescriptor(60, SpvOpCompositeConstruct, 0), 50, 27, 60,
{1});
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools