Updated spirv-tools.
This commit is contained in:
parent
4d3fcfd4c7
commit
06e2677783
@ -1 +1 @@
|
||||
"v2020.5", "SPIRV-Tools v2020.5 1a2bfbb06fe9545a7aa26e85e4b92b876ca316fd"
|
||||
"v2020.5", "SPIRV-Tools v2020.5 46c9719f1b46f358df41b11c2575182cb5befbd5"
|
||||
|
2
3rdparty/spirv-tools/source/cfa.h
vendored
2
3rdparty/spirv-tools/source/cfa.h
vendored
@ -127,7 +127,7 @@ class CFA {
|
||||
template <class BB>
|
||||
bool CFA<BB>::FindInWorkList(const std::vector<block_info>& work_list,
|
||||
uint32_t id) {
|
||||
for (const auto b : work_list) {
|
||||
for (const auto& b : work_list) {
|
||||
if (b.block->id() == id) return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -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
|
||||
|
24
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
24
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@ -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_;
|
||||
|
118
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
118
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@ -24,6 +24,8 @@
|
||||
#include "source/fuzz/transformation_add_constant_null.h"
|
||||
#include "source/fuzz/transformation_add_constant_scalar.h"
|
||||
#include "source/fuzz/transformation_add_global_undef.h"
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_loop_preheader.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
@ -516,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>>*
|
||||
@ -595,5 +617,101 @@ opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader(
|
||||
return &*function->FindBlock(preheader_id);
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateLocalVariable(
|
||||
uint32_t pointer_type_id, uint32_t function_id,
|
||||
bool pointee_value_is_irrelevant) {
|
||||
auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id);
|
||||
// No unused variables in release mode.
|
||||
(void)pointer_type;
|
||||
assert(pointer_type && pointer_type->AsPointer() &&
|
||||
pointer_type->AsPointer()->storage_class() ==
|
||||
SpvStorageClassFunction &&
|
||||
"The pointer_type_id must refer to a defined pointer type with "
|
||||
"storage class Function");
|
||||
auto function = fuzzerutil::FindFunction(GetIRContext(), function_id);
|
||||
assert(function && "The function must be defined.");
|
||||
|
||||
// First we try to find a suitable existing variable.
|
||||
// All of the local variable declarations are located in the first block.
|
||||
for (auto& instruction : *function->begin()) {
|
||||
if (instruction.opcode() != SpvOpVariable) {
|
||||
continue;
|
||||
}
|
||||
// The existing OpVariable must have type |pointer_type_id|.
|
||||
if (instruction.type_id() != pointer_type_id) {
|
||||
continue;
|
||||
}
|
||||
// Check if the found variable is marked with PointeeValueIsIrrelevant
|
||||
// according to |pointee_value_is_irrelevant|.
|
||||
if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
instruction.result_id()) != pointee_value_is_irrelevant) {
|
||||
continue;
|
||||
}
|
||||
return instruction.result_id();
|
||||
}
|
||||
|
||||
// No such variable was found. Apply a transformation to get one.
|
||||
uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
uint32_t result_id = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
result_id, pointer_type_id, function_id,
|
||||
FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant),
|
||||
pointee_value_is_irrelevant));
|
||||
return result_id;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateGlobalVariable(
|
||||
uint32_t pointer_type_id, bool pointee_value_is_irrelevant) {
|
||||
auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id);
|
||||
// No unused variables in release mode.
|
||||
(void)pointer_type;
|
||||
assert(
|
||||
pointer_type && pointer_type->AsPointer() &&
|
||||
(pointer_type->AsPointer()->storage_class() == SpvStorageClassPrivate ||
|
||||
pointer_type->AsPointer()->storage_class() ==
|
||||
SpvStorageClassWorkgroup) &&
|
||||
"The pointer_type_id must refer to a defined pointer type with storage "
|
||||
"class Private or Workgroup");
|
||||
|
||||
// First we try to find a suitable existing variable.
|
||||
for (auto& instruction : GetIRContext()->module()->types_values()) {
|
||||
if (instruction.opcode() != SpvOpVariable) {
|
||||
continue;
|
||||
}
|
||||
// The existing OpVariable must have type |pointer_type_id|.
|
||||
if (instruction.type_id() != pointer_type_id) {
|
||||
continue;
|
||||
}
|
||||
// Check if the found variable is marked with PointeeValueIsIrrelevant
|
||||
// according to |pointee_value_is_irrelevant|.
|
||||
if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
instruction.result_id()) != pointee_value_is_irrelevant) {
|
||||
continue;
|
||||
}
|
||||
return instruction.result_id();
|
||||
}
|
||||
|
||||
// No such variable was found. Apply a transformation to get one.
|
||||
uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
uint32_t result_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// A variable with storage class Workgroup shouldn't have an initializer.
|
||||
if (storage_class == SpvStorageClassWorkgroup) {
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
result_id, pointer_type_id, SpvStorageClassWorkgroup, 0,
|
||||
pointee_value_is_irrelevant));
|
||||
} else {
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
result_id, pointer_type_id, SpvStorageClassPrivate,
|
||||
FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant),
|
||||
pointee_value_is_irrelevant));
|
||||
}
|
||||
return result_id;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
24
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
24
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@ -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
|
||||
@ -293,6 +296,27 @@ class FuzzerPass {
|
||||
// reachable in the CFG (and thus has at least 2 predecessors).
|
||||
opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id);
|
||||
|
||||
// Returns the id of an available local variable (storage class Function) with
|
||||
// the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. If there is no such variable, it creates one
|
||||
// in the |function| adding a zero initializer constant that is irrelevant.
|
||||
// The new variable has the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. The function returns the id of the created
|
||||
// variable.
|
||||
uint32_t FindOrCreateLocalVariable(uint32_t pointer_type_id,
|
||||
uint32_t function_id,
|
||||
bool pointee_value_is_irrelevant);
|
||||
|
||||
// Returns the id of an available global variable (storage class Private or
|
||||
// Workgroup) with the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. If there is no such variable, it creates
|
||||
// one, adding a zero initializer constant that is irrelevant. The new
|
||||
// variable has the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. The function returns the id of the created
|
||||
// variable.
|
||||
uint32_t FindOrCreateGlobalVariable(uint32_t pointer_type_id,
|
||||
bool pointee_value_is_irrelevant);
|
||||
|
||||
private:
|
||||
opt::IRContext* ir_context_;
|
||||
TransformationContext* transformation_context_;
|
||||
|
236
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
vendored
Normal file
236
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp
vendored
Normal file
@ -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
|
45
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h
vendored
Normal file
45
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h
vendored
Normal file
@ -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
|
@ -69,14 +69,66 @@ void FuzzerPassAddParameters::Apply() {
|
||||
auto num_new_parameters =
|
||||
GetFuzzerContext()->GetRandomNumberOfNewParameters(
|
||||
GetNumberOfParameters(function));
|
||||
|
||||
for (uint32_t i = 0; i < num_new_parameters; ++i) {
|
||||
auto current_type_id =
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
|
||||
auto current_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(current_type_id);
|
||||
std::map<uint32_t, uint32_t> call_parameter_ids;
|
||||
|
||||
// Consider the case when a pointer type was selected.
|
||||
if (current_type->kind() == opt::analysis::Type::kPointer) {
|
||||
auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
|
||||
GetIRContext(), current_type_id);
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassFunction: {
|
||||
// In every caller find or create a local variable that has the
|
||||
// selected type.
|
||||
for (auto* instr :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
auto block = GetIRContext()->get_instr_block(instr);
|
||||
auto function_id = block->GetParent()->result_id();
|
||||
uint32_t variable_id =
|
||||
FindOrCreateLocalVariable(current_type_id, function_id, true);
|
||||
call_parameter_ids[instr->result_id()] = variable_id;
|
||||
}
|
||||
} break;
|
||||
case SpvStorageClassPrivate:
|
||||
case SpvStorageClassWorkgroup: {
|
||||
// If there exists at least one caller, find or create a global
|
||||
// variable that has the selected type.
|
||||
std::vector<opt::Instruction*> callers =
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id());
|
||||
if (!callers.empty()) {
|
||||
uint32_t variable_id =
|
||||
FindOrCreateGlobalVariable(current_type_id, true);
|
||||
for (auto* instr : callers) {
|
||||
call_parameter_ids[instr->result_id()] = variable_id;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If there exists at least one caller, find or create a zero constant
|
||||
// that has the selected type.
|
||||
std::vector<opt::Instruction*> callers =
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id());
|
||||
if (!callers.empty()) {
|
||||
uint32_t constant_id =
|
||||
FindOrCreateZeroConstant(current_type_id, true);
|
||||
for (auto* instr :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
call_parameter_ids[instr->result_id()] = constant_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationAddParameter(
|
||||
function.result_id(), GetFuzzerContext()->GetFreshId(),
|
||||
// We mark the constant as irrelevant so that we can replace it with a
|
||||
// more interesting value later.
|
||||
FindOrCreateZeroConstant(
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)],
|
||||
true),
|
||||
current_type_id, std::move(call_parameter_ids),
|
||||
GetFuzzerContext()->GetFreshId()));
|
||||
}
|
||||
}
|
||||
|
@ -1234,23 +1234,19 @@ bool FuzzerPassDonateModules::MaybeAddLivesafeFunction(
|
||||
}
|
||||
}
|
||||
|
||||
// If the function contains OpKill or OpUnreachable instructions, and has
|
||||
// non-void return type, then we need a value %v to use in order to turn
|
||||
// these into instructions of the form OpReturn %v.
|
||||
uint32_t kill_unreachable_return_value_id;
|
||||
// If |function_to_donate| has non-void return type and contains an
|
||||
// OpKill/OpUnreachable instruction, then a value is needed in order to turn
|
||||
// these into instructions of the form OpReturnValue %value_id.
|
||||
uint32_t kill_unreachable_return_value_id = 0;
|
||||
auto function_return_type_inst =
|
||||
donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id());
|
||||
if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
|
||||
// The return type is void, so we don't need a return value.
|
||||
kill_unreachable_return_value_id = 0;
|
||||
} else {
|
||||
// We do need a return value; we use zero.
|
||||
assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
|
||||
"Function return type must not be a pointer.");
|
||||
if (function_return_type_inst->opcode() != SpvOpTypeVoid &&
|
||||
fuzzerutil::FunctionContainsOpKillOrUnreachable(function_to_donate)) {
|
||||
kill_unreachable_return_value_id = FindOrCreateZeroConstant(
|
||||
original_id_to_donated_id.at(function_return_type_inst->result_id()),
|
||||
false);
|
||||
}
|
||||
|
||||
// Add the function in a livesafe manner.
|
||||
ApplyTransformation(TransformationAddFunction(
|
||||
donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
|
||||
|
@ -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);
|
||||
|
||||
|
11
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
11
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@ -476,6 +476,16 @@ opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FunctionContainsOpKillOrUnreachable(const opt::Function& function) {
|
||||
for (auto& block : function) {
|
||||
if (block.terminator()->opcode() == SpvOpKill ||
|
||||
block.terminator()->opcode() == SpvOpUnreachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
|
||||
for (auto& entry_point : context->module()->entry_points()) {
|
||||
if (entry_point.GetSingleWordInOperand(1) == function_id) {
|
||||
@ -1343,6 +1353,5 @@ opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -179,6 +179,10 @@ opt::Instruction* GetFunctionType(opt::IRContext* context,
|
||||
// function exists.
|
||||
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
|
||||
|
||||
// Returns true if |function| has a block that the termination instruction is
|
||||
// OpKill or OpUnreachable.
|
||||
bool FunctionContainsOpKillOrUnreachable(const opt::Function& function);
|
||||
|
||||
// Returns |true| if one of entry points has function id |function_id|.
|
||||
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
|
||||
|
||||
@ -494,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.
|
||||
}
|
||||
}
|
||||
@ -754,15 +755,17 @@ message TransformationAddParameter {
|
||||
// Fresh id for a new parameter.
|
||||
uint32 parameter_fresh_id = 2;
|
||||
|
||||
// Result id of the instruction, used to initializer new parameter
|
||||
// in function calls. Type id of that instruction is the type id of the parameter.
|
||||
// It may not be OpTypeVoid.
|
||||
uint32 initializer_id = 3;
|
||||
// Type id for a new parameter.
|
||||
uint32 parameter_type_id = 3;
|
||||
|
||||
// A map that maps from the OpFunctionCall id to the id that will be passed as the new
|
||||
// parameter at that call site. It must have the same type as that of the new parameter.
|
||||
repeated UInt32Pair call_parameter_ids = 4;
|
||||
|
||||
// A fresh id for a new function type. This might not be used
|
||||
// if a required function type already exists or if we can change
|
||||
// the old function type.
|
||||
uint32 function_type_fresh_id = 4;
|
||||
uint32 function_type_fresh_id = 5;
|
||||
|
||||
}
|
||||
|
||||
@ -1019,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>(
|
||||
|
@ -64,13 +64,12 @@ bool TransformationAddConstantScalar::IsApplicable(
|
||||
void TransformationAddConstantScalar::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
opt::Instruction::OperandList operand_list;
|
||||
for (auto word : message_.word()) {
|
||||
operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
|
||||
}
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(),
|
||||
operand_list));
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
std::vector<uint32_t>(message_.word().begin(),
|
||||
message_.word().end())}})));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
|
@ -78,6 +78,27 @@ bool TransformationAddDeadBlock::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// |existing_block| must be reachable.
|
||||
opt::DominatorAnalysis* dominator_analysis =
|
||||
ir_context->GetDominatorAnalysis(existing_block->GetParent());
|
||||
if (!dominator_analysis->IsReachable(existing_block->id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(existing_block->id() != successor_block_id &&
|
||||
"|existing_block| must be different from |successor_block_id|");
|
||||
|
||||
// Even though we know |successor_block_id| is not a merge block, it might
|
||||
// still have multiple predecessors because divergent control flow is allowed
|
||||
// to converge early (before the merge block). In this case, when we create
|
||||
// the selection construct, its header |existing_block| will not dominate the
|
||||
// merge block |successor_block_id|, which is invalid. Thus, |existing_block|
|
||||
// must dominate |successor_block_id|.
|
||||
if (!dominator_analysis->Dominates(existing_block->id(),
|
||||
successor_block_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -24,17 +24,20 @@ TransformationAddParameter::TransformationAddParameter(
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddParameter::TransformationAddParameter(
|
||||
uint32_t function_id, uint32_t parameter_fresh_id, uint32_t initializer_id,
|
||||
uint32_t function_id, uint32_t parameter_fresh_id,
|
||||
uint32_t parameter_type_id, std::map<uint32_t, uint32_t> call_parameter_ids,
|
||||
uint32_t function_type_fresh_id) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_parameter_fresh_id(parameter_fresh_id);
|
||||
message_.set_initializer_id(initializer_id);
|
||||
message_.set_parameter_type_id(parameter_type_id);
|
||||
*message_.mutable_call_parameter_ids() =
|
||||
fuzzerutil::MapToRepeatedUInt32Pair(call_parameter_ids);
|
||||
message_.set_function_type_fresh_id(function_type_fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationAddParameter::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that function exists
|
||||
// Check that function exists.
|
||||
const auto* function =
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
if (!function ||
|
||||
@ -42,22 +45,59 @@ bool TransformationAddParameter::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |initializer_id| is valid.
|
||||
const auto* initializer_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
|
||||
|
||||
if (!initializer_inst) {
|
||||
// The type must be supported.
|
||||
uint32_t new_parameter_type_id = message_.parameter_type_id();
|
||||
auto new_parameter_type =
|
||||
ir_context->get_type_mgr()->GetType(new_parameter_type_id);
|
||||
if (!new_parameter_type) {
|
||||
return false;
|
||||
}
|
||||
if (!IsParameterTypeSupported(*new_parameter_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that initializer's type is valid.
|
||||
const auto* initializer_type =
|
||||
ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
|
||||
// Iterate over all callers.
|
||||
std::map<uint32_t, uint32_t> call_parameter_ids_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
|
||||
for (auto* instr :
|
||||
fuzzerutil::GetCallers(ir_context, message_.function_id())) {
|
||||
uint32_t caller_id = instr->result_id();
|
||||
|
||||
if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) {
|
||||
return false;
|
||||
// If there is no entry for this caller, return false.
|
||||
if (call_parameter_ids_map.find(caller_id) ==
|
||||
call_parameter_ids_map.end()) {
|
||||
return false;
|
||||
}
|
||||
uint32_t value_id = call_parameter_ids_map[caller_id];
|
||||
|
||||
auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id);
|
||||
if (!value_instr) {
|
||||
return false;
|
||||
}
|
||||
// If the id of the value of the map is not available before the caller,
|
||||
// return false.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3722):
|
||||
// This can potentially trigger a bug if the caller is in an
|
||||
// unreachable block. fuzzerutil::IdIsAvailableBeforeInstruction uses
|
||||
// dominator analysis to check that value_id is available and the
|
||||
// domination rules are not defined for unreachable blocks.
|
||||
// The following code should be refactored.
|
||||
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, instr,
|
||||
value_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The type of the value must be defined.
|
||||
uint32_t value_type_id = fuzzerutil::GetTypeId(ir_context, value_id);
|
||||
if (!value_type_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Type of every value of the map must be the same for all callers.
|
||||
if (new_parameter_type_id != value_type_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
|
||||
fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
|
||||
message_.parameter_fresh_id() != message_.function_type_fresh_id();
|
||||
@ -66,13 +106,17 @@ bool TransformationAddParameter::IsApplicable(
|
||||
void TransformationAddParameter::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Find the function that will be transformed
|
||||
// Find the function that will be transformed.
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
const auto new_parameter_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
|
||||
assert(new_parameter_type_id != 0 && "Initializer has invalid type");
|
||||
std::map<uint32_t, uint32_t> call_parameter_ids_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids());
|
||||
|
||||
uint32_t new_parameter_type_id = message_.parameter_type_id();
|
||||
auto new_parameter_type =
|
||||
ir_context->get_type_mgr()->GetType(new_parameter_type_id);
|
||||
assert(new_parameter_type && "New parameter has invalid type.");
|
||||
|
||||
// Add new parameters to the function.
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
@ -81,17 +125,24 @@ void TransformationAddParameter::Apply(
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
|
||||
|
||||
// Mark new parameter as irrelevant so that we can replace its use with some
|
||||
// other id.
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.parameter_fresh_id());
|
||||
// If the |new_parameter_type_id| is not a pointer type, mark id as
|
||||
// irrelevant so that we can replace its use with some other id. If the
|
||||
// |new_parameter_type_id| is a pointer type, we cannot mark it with
|
||||
// IdIsIrrelevant, because this pointer might be replaced by a pointer from
|
||||
// original shader. This would change the semantics of the module. In the case
|
||||
// of a pointer type we mark it with PointeeValueIsIrrelevant.
|
||||
if (new_parameter_type->kind() != opt::analysis::Type::kPointer) {
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.parameter_fresh_id());
|
||||
} else {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.parameter_fresh_id());
|
||||
}
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID, {call_parameter_ids_map[inst->result_id()]}});
|
||||
}
|
||||
|
||||
// Update function's type.
|
||||
@ -145,6 +196,19 @@ bool TransformationAddParameter::IsParameterTypeSupported(
|
||||
[](const opt::analysis::Type* element_type) {
|
||||
return IsParameterTypeSupported(*element_type);
|
||||
});
|
||||
case opt::analysis::Type::kPointer: {
|
||||
auto storage_class = type.AsPointer()->storage_class();
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassPrivate:
|
||||
case SpvStorageClassFunction:
|
||||
case SpvStorageClassWorkgroup: {
|
||||
auto pointee_type = type.AsPointer()->pointee_type();
|
||||
return IsParameterTypeSupported(*pointee_type);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -29,14 +29,19 @@ class TransformationAddParameter : public Transformation {
|
||||
const protobufs::TransformationAddParameter& message);
|
||||
|
||||
TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id,
|
||||
uint32_t initializer_id,
|
||||
uint32_t parameter_type_id,
|
||||
std::map<uint32_t, uint32_t> call_parameter_ids,
|
||||
uint32_t function_type_fresh_id);
|
||||
|
||||
// - |function_id| must be a valid result id of some non-entry-point function
|
||||
// in the module.
|
||||
// - |initializer_id| must be a valid result id of some instruction in the
|
||||
// module. Instruction's type must be supported by this transformation
|
||||
// as specified by IsParameterTypeSupported function.
|
||||
// - |parameter_type_id| is a type id of the new parameter. The type must be
|
||||
// supported by this transformation as specified by IsParameterTypeSupported
|
||||
// function.
|
||||
// - |call_parameter_id| must map from every id of an OpFunctionCall
|
||||
// instruction of this function to the id that will be passed as the new
|
||||
// parameter at that call site. There could be no callers, therefore this
|
||||
// map can be empty.
|
||||
// - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
|
||||
// not equal.
|
||||
bool IsApplicable(
|
||||
@ -46,8 +51,8 @@ class TransformationAddParameter : public Transformation {
|
||||
// - Creates a new OpFunctionParameter instruction with result id
|
||||
// |parameter_fresh_id| for the function with |function_id|.
|
||||
// - Adjusts function's type to include a new parameter.
|
||||
// - Adds |initializer_id| as a new operand to every OpFunctionCall
|
||||
// instruction that calls the function.
|
||||
// - Adds an argument to every caller of the function to account for the added
|
||||
// parameter. The argument is the value in |call_parameter_id| map.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
226
3rdparty/spirv-tools/source/fuzz/transformation_composite_insert.cpp
vendored
Normal file
226
3rdparty/spirv-tools/source/fuzz/transformation_composite_insert.cpp
vendored
Normal file
@ -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
|
72
3rdparty/spirv-tools/source/fuzz/transformation_composite_insert.h
vendored
Normal file
72
3rdparty/spirv-tools/source/fuzz/transformation_composite_insert.h
vendored
Normal file
@ -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
|
@ -533,6 +533,17 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
AddToWorklist(dec);
|
||||
}
|
||||
|
||||
// Add DebugScope and DebugInlinedAt for |liveInst| to the work list.
|
||||
if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
|
||||
auto* scope = get_def_use_mgr()->GetDef(
|
||||
liveInst->GetDebugScope().GetLexicalScope());
|
||||
AddToWorklist(scope);
|
||||
}
|
||||
if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) {
|
||||
auto* inlined_at =
|
||||
get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt());
|
||||
AddToWorklist(inlined_at);
|
||||
}
|
||||
worklist_.pop();
|
||||
}
|
||||
|
||||
|
23
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
23
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
@ -135,6 +135,7 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
|
||||
}
|
||||
return it->second;
|
||||
};
|
||||
uint32_t next_id = context()->module()->IdBound();
|
||||
Instruction* folded_inst =
|
||||
context()->get_instruction_folder().FoldInstructionToConstant(instr,
|
||||
map_func);
|
||||
@ -143,6 +144,14 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
|
||||
// instructions. When folding we can only generate new constants.
|
||||
assert(folded_inst->IsConstant() && "CCP is only interested in constant.");
|
||||
values_[instr->result_id()] = folded_inst->result_id();
|
||||
|
||||
// If the folded instruction has just been created, its result ID will
|
||||
// match the previous ID bound. When this happens, we need to indicate
|
||||
// that CCP has modified the IR, independently of whether the constant is
|
||||
// actually propagated. See
|
||||
// https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 for details.
|
||||
if (folded_inst->result_id() == next_id) created_new_constant_ = true;
|
||||
|
||||
return SSAPropagator::kInteresting;
|
||||
}
|
||||
|
||||
@ -266,16 +275,22 @@ SSAPropagator::PropStatus CCPPass::VisitInstruction(Instruction* instr,
|
||||
}
|
||||
|
||||
bool CCPPass::ReplaceValues() {
|
||||
bool retval = false;
|
||||
// Even if we make no changes to the function's IR, propagation may have
|
||||
// created new constants. Even if those constants cannot be replaced in
|
||||
// the IR, the constant definition itself is a change. To reflect this,
|
||||
// we initialize the IR changed indicator with the value of the
|
||||
// created_new_constant_ indicator. For an example, see the bug reported
|
||||
// in https://github.com/KhronosGroup/SPIRV-Tools/issues/3636.
|
||||
bool changed_ir = created_new_constant_;
|
||||
for (const auto& it : values_) {
|
||||
uint32_t id = it.first;
|
||||
uint32_t cst_id = it.second;
|
||||
if (!IsVaryingValue(cst_id) && id != cst_id) {
|
||||
context()->KillNamesAndDecorates(id);
|
||||
retval |= context()->ReplaceAllUsesWith(id, cst_id);
|
||||
changed_ir |= context()->ReplaceAllUsesWith(id, cst_id);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
return changed_ir;
|
||||
}
|
||||
|
||||
bool CCPPass::PropagateConstants(Function* fp) {
|
||||
@ -313,6 +328,8 @@ void CCPPass::Initialize() {
|
||||
values_[inst.result_id()] = kVaryingSSAId;
|
||||
}
|
||||
}
|
||||
|
||||
created_new_constant_ = false;
|
||||
}
|
||||
|
||||
Pass::Status CCPPass::Process() {
|
||||
|
3
3rdparty/spirv-tools/source/opt/ccp_pass.h
vendored
3
3rdparty/spirv-tools/source/opt/ccp_pass.h
vendored
@ -105,6 +105,9 @@ class CCPPass : public MemPass {
|
||||
|
||||
// Propagator engine used.
|
||||
std::unique_ptr<SSAPropagator> propagator_;
|
||||
|
||||
// True if the pass created new constant instructions during propagation.
|
||||
bool created_new_constant_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
12
3rdparty/spirv-tools/source/opt/function.cpp
vendored
12
3rdparty/spirv-tools/source/opt/function.cpp
vendored
@ -227,6 +227,18 @@ BasicBlock* Function::InsertBasicBlockBefore(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Function::HasEarlyReturn() const {
|
||||
auto post_dominator_analysis =
|
||||
blocks_.front()->GetLabel()->context()->GetPostDominatorAnalysis(this);
|
||||
for (auto& block : blocks_) {
|
||||
if (spvOpcodeIsReturn(block->tail()->opcode()) &&
|
||||
!post_dominator_analysis->Dominates(block.get(), entry().get())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Function::IsRecursive() const {
|
||||
IRContext* ctx = blocks_.front()->GetLabel()->context();
|
||||
IRContext::ProcessFunction mark_visited = [this](Function* fp) {
|
||||
|
5
3rdparty/spirv-tools/source/opt/function.h
vendored
5
3rdparty/spirv-tools/source/opt/function.h
vendored
@ -158,7 +158,10 @@ class Function {
|
||||
BasicBlock* InsertBasicBlockBefore(std::unique_ptr<BasicBlock>&& new_block,
|
||||
BasicBlock* position);
|
||||
|
||||
// Return true if the function calls itself either directly or indirectly.
|
||||
// Returns true if the function has a return block other than the exit block.
|
||||
bool HasEarlyReturn() const;
|
||||
|
||||
// Returns true if the function calls itself either directly or indirectly.
|
||||
bool IsRecursive() const;
|
||||
|
||||
// Pretty-prints all the basic blocks in this function into a std::string.
|
||||
|
@ -179,7 +179,7 @@ spv_result_t ValidateOperandDebugType(
|
||||
return true;
|
||||
}
|
||||
return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
|
||||
dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
|
||||
dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate;
|
||||
};
|
||||
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
|
||||
return SPV_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user