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:
Родитель
582c276d43
Коммит
a711c594b8
|
@ -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
|
Загрузка…
Ссылка в новой задаче