Updated spirv-tools.
This commit is contained in:
parent
a9e1d1a8ce
commit
f16ab74475
@ -1 +1 @@
|
||||
"v2020.5", "SPIRV-Tools v2020.5 276598ad50d33f1d1a56311520b17390a6bed635"
|
||||
"v2020.5", "SPIRV-Tools v2020.5 1a2bfbb06fe9545a7aa26e85e4b92b876ca316fd"
|
||||
|
2
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
2
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
@ -414,7 +414,7 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
20
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
20
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@ -50,6 +50,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_synonyms.h
|
||||
fuzzer_pass_add_loads.h
|
||||
fuzzer_pass_add_local_variables.h
|
||||
fuzzer_pass_add_loop_preheaders.h
|
||||
fuzzer_pass_add_no_contraction_decorations.h
|
||||
fuzzer_pass_add_parameters.h
|
||||
fuzzer_pass_add_relaxed_decorations.h
|
||||
@ -67,13 +68,17 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_invert_comparison_operators.h
|
||||
fuzzer_pass_interchange_signedness_of_integer_operands.h
|
||||
fuzzer_pass_interchange_zero_like_constants.h
|
||||
fuzzer_pass_make_vector_operations_dynamic.h
|
||||
fuzzer_pass_merge_blocks.h
|
||||
fuzzer_pass_obfuscate_constants.h
|
||||
fuzzer_pass_outline_functions.h
|
||||
fuzzer_pass_permute_blocks.h
|
||||
fuzzer_pass_permute_function_parameters.h
|
||||
fuzzer_pass_permute_instructions.h
|
||||
fuzzer_pass_permute_phi_operands.h
|
||||
fuzzer_pass_propagate_instructions_up.h
|
||||
fuzzer_pass_push_ids_through_variables.h
|
||||
fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
|
||||
fuzzer_pass_replace_copy_memories_with_loads_stores.h
|
||||
fuzzer_pass_replace_copy_objects_with_stores_loads.h
|
||||
fuzzer_pass_replace_linear_algebra_instructions.h
|
||||
@ -132,13 +137,17 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_function_call.h
|
||||
transformation_invert_comparison_operator.h
|
||||
transformation_load.h
|
||||
transformation_make_vector_operation_dynamic.h
|
||||
transformation_merge_blocks.h
|
||||
transformation_move_block_down.h
|
||||
transformation_move_instruction_down.h
|
||||
transformation_outline_function.h
|
||||
transformation_permute_function_parameters.h
|
||||
transformation_permute_phi_operands.h
|
||||
transformation_propagate_instruction_up.h
|
||||
transformation_push_id_through_variable.h
|
||||
transformation_record_synonymous_constants.h
|
||||
transformation_replace_add_sub_mul_with_carrying_extended.h
|
||||
transformation_replace_boolean_constant_with_constant_binary.h
|
||||
transformation_replace_constant_with_uniform.h
|
||||
transformation_replace_copy_memory_with_load_store.h
|
||||
@ -181,6 +190,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_synonyms.cpp
|
||||
fuzzer_pass_add_loads.cpp
|
||||
fuzzer_pass_add_local_variables.cpp
|
||||
fuzzer_pass_add_loop_preheaders.cpp
|
||||
fuzzer_pass_add_no_contraction_decorations.cpp
|
||||
fuzzer_pass_add_parameters.cpp
|
||||
fuzzer_pass_add_relaxed_decorations.cpp
|
||||
@ -198,13 +208,17 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_invert_comparison_operators.cpp
|
||||
fuzzer_pass_interchange_signedness_of_integer_operands.cpp
|
||||
fuzzer_pass_interchange_zero_like_constants.cpp
|
||||
fuzzer_pass_make_vector_operations_dynamic.cpp
|
||||
fuzzer_pass_merge_blocks.cpp
|
||||
fuzzer_pass_obfuscate_constants.cpp
|
||||
fuzzer_pass_outline_functions.cpp
|
||||
fuzzer_pass_permute_blocks.cpp
|
||||
fuzzer_pass_permute_function_parameters.cpp
|
||||
fuzzer_pass_permute_instructions.cpp
|
||||
fuzzer_pass_permute_phi_operands.cpp
|
||||
fuzzer_pass_propagate_instructions_up.cpp
|
||||
fuzzer_pass_push_ids_through_variables.cpp
|
||||
fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
|
||||
fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
|
||||
fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
|
||||
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
||||
@ -262,13 +276,17 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_function_call.cpp
|
||||
transformation_invert_comparison_operator.cpp
|
||||
transformation_load.cpp
|
||||
transformation_make_vector_operation_dynamic.cpp
|
||||
transformation_merge_blocks.cpp
|
||||
transformation_move_block_down.cpp
|
||||
transformation_move_instruction_down.cpp
|
||||
transformation_outline_function.cpp
|
||||
transformation_permute_function_parameters.cpp
|
||||
transformation_permute_phi_operands.cpp
|
||||
transformation_propagate_instruction_up.cpp
|
||||
transformation_push_id_through_variable.cpp
|
||||
transformation_record_synonymous_constants.cpp
|
||||
transformation_replace_add_sub_mul_with_carrying_extended.cpp
|
||||
transformation_replace_boolean_constant_with_constant_binary.cpp
|
||||
transformation_replace_constant_with_uniform.cpp
|
||||
transformation_replace_copy_memory_with_load_store.cpp
|
||||
@ -292,7 +310,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
16
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
16
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@ -32,6 +32,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_loads.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
|
||||
#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_stores.h"
|
||||
@ -49,12 +50,15 @@
|
||||
#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
|
||||
#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h"
|
||||
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_outline_functions.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_instructions.h"
|
||||
#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_linear_algebra_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
|
||||
@ -239,6 +243,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddLocalVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddLoopPreheaders>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddParameters>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
@ -266,6 +273,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassInvertComparisonOperators>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassMakeVectorOperationsDynamic>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassMergeBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
@ -281,6 +291,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteInstructions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPropagateInstructionsUp>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
|
@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoopPreheader = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
|
||||
5, 70};
|
||||
@ -72,13 +73,20 @@ const std::pair<uint32_t, uint32_t>
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
|
||||
20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMakingVectorOperationDynamic = {
|
||||
20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
|
||||
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};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
|
||||
@ -158,6 +166,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
chance_of_adding_global_variable_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
|
||||
chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
|
||||
chance_of_adding_loop_preheader_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingLoopPreheader);
|
||||
chance_of_adding_image_sample_unused_components_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
|
||||
chance_of_adding_local_variable_ =
|
||||
@ -207,6 +217,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
|
||||
chance_of_making_donor_livesafe_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
|
||||
chance_of_making_vector_operation_dynamic_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfMakingVectorOperationDynamic);
|
||||
chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
|
||||
chance_of_moving_block_down_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
|
||||
@ -214,12 +226,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
|
||||
chance_of_outlining_function_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
|
||||
chance_of_permuting_instructions_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingInstructions);
|
||||
chance_of_permuting_parameters_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
|
||||
chance_of_permuting_phi_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
|
||||
chance_of_propagating_instructions_up_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsUp);
|
||||
chance_of_pushing_id_through_variable_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
|
||||
chance_of_replacing_add_sub_mul_with_carrying_extended_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended);
|
||||
chance_of_replacing_copy_memory_with_load_store_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
|
||||
chance_of_replacing_copyobject_with_store_load_ =
|
||||
|
@ -136,6 +136,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfAddingLocalVariable() {
|
||||
return chance_of_adding_local_variable_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingLoopPreheader() {
|
||||
return chance_of_adding_loop_preheader_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingMatrixType() {
|
||||
return chance_of_adding_matrix_type_;
|
||||
}
|
||||
@ -198,6 +201,9 @@ class FuzzerContext {
|
||||
uint32_t ChanceOfMakingDonorLivesafe() {
|
||||
return chance_of_making_donor_livesafe_;
|
||||
}
|
||||
uint32_t GetChanceOfMakingVectorOperationDynamic() {
|
||||
return chance_of_making_vector_operation_dynamic_;
|
||||
}
|
||||
uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
|
||||
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
|
||||
uint32_t GetChanceOfObfuscatingConstant() {
|
||||
@ -206,15 +212,24 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfOutliningFunction() {
|
||||
return chance_of_outlining_function_;
|
||||
}
|
||||
uint32_t GetChanceOfPermutingInstructions() {
|
||||
return chance_of_permuting_instructions_;
|
||||
}
|
||||
uint32_t GetChanceOfPermutingParameters() {
|
||||
return chance_of_permuting_parameters_;
|
||||
}
|
||||
uint32_t GetChanceOfPermutingPhiOperands() {
|
||||
return chance_of_permuting_phi_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfPropagatingInstructionsUp() {
|
||||
return chance_of_propagating_instructions_up_;
|
||||
}
|
||||
uint32_t GetChanceOfPushingIdThroughVariable() {
|
||||
return chance_of_pushing_id_through_variable_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
|
||||
return chance_of_replacing_add_sub_mul_with_carrying_extended_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
|
||||
return chance_of_replacing_copy_memory_with_load_store_;
|
||||
}
|
||||
@ -336,6 +351,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_adding_image_sample_unused_components_;
|
||||
uint32_t chance_of_adding_load_;
|
||||
uint32_t chance_of_adding_local_variable_;
|
||||
uint32_t chance_of_adding_loop_preheader_;
|
||||
uint32_t chance_of_adding_matrix_type_;
|
||||
uint32_t chance_of_adding_no_contraction_decoration_;
|
||||
uint32_t chance_of_adding_parameters;
|
||||
@ -360,13 +376,17 @@ class FuzzerContext {
|
||||
uint32_t chance_of_interchanging_zero_like_constants_;
|
||||
uint32_t chance_of_inverting_comparison_operators_;
|
||||
uint32_t chance_of_making_donor_livesafe_;
|
||||
uint32_t chance_of_making_vector_operation_dynamic_;
|
||||
uint32_t chance_of_merging_blocks_;
|
||||
uint32_t chance_of_moving_block_down_;
|
||||
uint32_t chance_of_obfuscating_constant_;
|
||||
uint32_t chance_of_outlining_function_;
|
||||
uint32_t chance_of_permuting_instructions_;
|
||||
uint32_t chance_of_permuting_parameters_;
|
||||
uint32_t chance_of_permuting_phi_operands_;
|
||||
uint32_t chance_of_propagating_instructions_up_;
|
||||
uint32_t chance_of_pushing_id_through_variable_;
|
||||
uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_;
|
||||
uint32_t chance_of_replacing_copy_memory_with_load_store_;
|
||||
uint32_t chance_of_replacing_copyobject_with_store_load_;
|
||||
uint32_t chance_of_replacing_id_with_synonym_;
|
||||
|
62
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
62
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@ -24,6 +24,7 @@
|
||||
#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_loop_preheader.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
#include "source/fuzz/transformation_add_type_function.h"
|
||||
@ -533,5 +534,66 @@ void FuzzerPass::MaybeAddUseToReplace(
|
||||
std::make_pair(id_use_descriptor, replacement_id));
|
||||
}
|
||||
|
||||
opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader(
|
||||
uint32_t header_id) {
|
||||
auto header_block = fuzzerutil::MaybeFindBlock(GetIRContext(), header_id);
|
||||
|
||||
assert(header_block && header_block->IsLoopHeader() &&
|
||||
"|header_id| should be the label id of a loop header");
|
||||
|
||||
auto predecessors = GetIRContext()->cfg()->preds(header_id);
|
||||
|
||||
assert(predecessors.size() >= 2 &&
|
||||
"The block |header_id| should be reachable.");
|
||||
|
||||
auto function = header_block->GetParent();
|
||||
|
||||
if (predecessors.size() == 2) {
|
||||
// The header has a single out-of-loop predecessor, which could be a
|
||||
// preheader.
|
||||
|
||||
opt::BasicBlock* maybe_preheader;
|
||||
|
||||
if (GetIRContext()->GetDominatorAnalysis(function)->Dominates(
|
||||
header_id, predecessors[0])) {
|
||||
// The first predecessor is the back-edge block, because the header
|
||||
// dominates it, so the second one is out of the loop.
|
||||
maybe_preheader = &*function->FindBlock(predecessors[1]);
|
||||
} else {
|
||||
// The first predecessor is out of the loop.
|
||||
maybe_preheader = &*function->FindBlock(predecessors[0]);
|
||||
}
|
||||
|
||||
// |maybe_preheader| is a preheader if it branches unconditionally to
|
||||
// the header. We also require it not to be a loop header.
|
||||
if (maybe_preheader->terminator()->opcode() == SpvOpBranch &&
|
||||
!maybe_preheader->IsLoopHeader()) {
|
||||
return maybe_preheader;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to add a preheader.
|
||||
|
||||
// Get a fresh id for the preheader.
|
||||
uint32_t preheader_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// Get a fresh id for each OpPhi instruction, if there is more than one
|
||||
// out-of-loop predecessor.
|
||||
std::vector<uint32_t> phi_ids;
|
||||
if (predecessors.size() > 2) {
|
||||
header_block->ForEachPhiInst(
|
||||
[this, &phi_ids](opt::Instruction* /* unused */) {
|
||||
phi_ids.push_back(GetFuzzerContext()->GetFreshId());
|
||||
});
|
||||
}
|
||||
|
||||
// Add the preheader.
|
||||
ApplyTransformation(
|
||||
TransformationAddLoopPreheader(header_id, preheader_id, phi_ids));
|
||||
|
||||
// Make the newly-created preheader the new entry block.
|
||||
return &*function->FindBlock(preheader_id);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
10
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
10
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@ -283,6 +283,16 @@ class FuzzerPass {
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace);
|
||||
|
||||
// Returns the preheader of the loop with header |header_id|, which satisfies
|
||||
// all of the following conditions:
|
||||
// - It is the only out-of-loop predecessor of the header
|
||||
// - It unconditionally branches to the header
|
||||
// - It is not a loop header itself
|
||||
// If such preheader does not exist, a new one is added and returned.
|
||||
// Requires |header_id| to be the label id of a loop header block that is
|
||||
// reachable in the CFG (and thus has at least 2 predecessors).
|
||||
opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id);
|
||||
|
||||
private:
|
||||
opt::IRContext* ir_context_;
|
||||
TransformationContext* transformation_context_;
|
||||
|
@ -21,6 +21,26 @@
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) {
|
||||
switch (bit_width) {
|
||||
case 32:
|
||||
return true;
|
||||
case 64:
|
||||
return ir_context->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityFloat64) &&
|
||||
ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
|
||||
case 16:
|
||||
return ir_context->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityFloat16) &&
|
||||
ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
@ -76,8 +96,22 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
switch (opcode) {
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF: {
|
||||
auto candidate_instructions =
|
||||
GetIntegerInstructions(available_instructions);
|
||||
std::vector<const opt::Instruction*> candidate_instructions;
|
||||
for (const auto* inst :
|
||||
GetIntegerInstructions(available_instructions)) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "|inst| has invalid type");
|
||||
|
||||
if (const auto* vector_type = type->AsVector()) {
|
||||
type = vector_type->element_type();
|
||||
}
|
||||
|
||||
if (IsBitWidthSupported(GetIRContext(),
|
||||
type->AsInteger()->width())) {
|
||||
candidate_instructions.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate_instructions.empty()) {
|
||||
break;
|
||||
@ -112,20 +146,8 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
return;
|
||||
}
|
||||
case SpvOpBitcast: {
|
||||
std::vector<const opt::Instruction*> candidate_instructions;
|
||||
for (const auto* inst : available_instructions) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "Instruction has invalid type");
|
||||
if ((type->AsVector() &&
|
||||
(type->AsVector()->element_type()->AsInteger() ||
|
||||
type->AsVector()->element_type()->AsFloat())) ||
|
||||
type->AsInteger() || type->AsFloat()) {
|
||||
// We support OpBitcast for only scalars or vectors of
|
||||
// numerical type.
|
||||
candidate_instructions.push_back(inst);
|
||||
}
|
||||
}
|
||||
const auto candidate_instructions =
|
||||
GetNumericalInstructions(available_instructions);
|
||||
|
||||
if (!candidate_instructions.empty()) {
|
||||
const auto* operand_inst =
|
||||
@ -356,5 +378,36 @@ FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetNumericalInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
|
||||
for (auto* inst : instructions) {
|
||||
const auto* type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "Instruction has invalid type");
|
||||
|
||||
if (const auto* vector_type = type->AsVector()) {
|
||||
type = vector_type->element_type();
|
||||
}
|
||||
|
||||
if (!type->AsInteger() && !type->AsFloat()) {
|
||||
// Only numerical scalars or vectors of numerical components are
|
||||
// supported.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsBitWidthSupported(GetIRContext(), type->AsInteger()
|
||||
? type->AsInteger()->width()
|
||||
: type->AsFloat()->width())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(inst);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -51,6 +51,14 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
|
||||
std::vector<opt::Instruction*> GetBooleanInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Yields those instructions in |instructions| that have a scalar numerical or
|
||||
// a vector of numerical components type. Only 16, 32 and 64-bit numericals
|
||||
// are supported if both OpTypeInt and OpTypeFloat instructions can be created
|
||||
// with the specified width (e.g. for 16-bit types both Float16 and Int16
|
||||
// capabilities must be present).
|
||||
std::vector<opt::Instruction*> GetNumericalInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Requires that |instructions| are scalars or vectors of some type. Returns
|
||||
// only those instructions whose width is |width|. If |width| is 1 this means
|
||||
// the scalars.
|
||||
|
66
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
vendored
Normal file
66
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
// 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_loop_preheaders.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_loop_preheader.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddLoopPreheaders::~FuzzerPassAddLoopPreheaders() = default;
|
||||
|
||||
void FuzzerPassAddLoopPreheaders::Apply() {
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// Keep track of all the loop headers we want to add a preheader to.
|
||||
std::vector<uint32_t> loop_header_ids_to_consider;
|
||||
for (auto& block : function) {
|
||||
// We only care about loop headers.
|
||||
if (!block.IsLoopHeader()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Randomly decide whether to consider this header.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingLoopPreheader())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We exclude loop headers with just one predecessor (the back-edge block)
|
||||
// because they are unreachable.
|
||||
if (GetIRContext()->cfg()->preds(block.id()).size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loop_header_ids_to_consider.push_back(block.id());
|
||||
}
|
||||
|
||||
for (uint32_t header_id : loop_header_ids_to_consider) {
|
||||
// If not already present, add a preheader which is not also a loop
|
||||
// header.
|
||||
GetOrCreateSimpleLoopPreheader(header_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
43
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h
vendored
Normal file
43
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass that randomly adds simple loop preheaders to loops that do not
|
||||
// have one. A simple loop preheader is a block that:
|
||||
// - is the only out-of-loop predecessor of the header
|
||||
// - branches unconditionally to the header
|
||||
// - is not a loop header itself
|
||||
class FuzzerPassAddLoopPreheaders : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddLoopPreheaders(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddLoopPreheaders();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H
|
@ -54,12 +54,12 @@ void FuzzerPassCopyObjects::Apply() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> relevant_instructions =
|
||||
FindAvailableInstructions(
|
||||
const auto relevant_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
return fuzzerutil::CanMakeSynonymOf(
|
||||
ir_context, *GetTransformationContext(), inst);
|
||||
return TransformationAddSynonym::IsInstructionValid(
|
||||
ir_context, *GetTransformationContext(), inst,
|
||||
protobufs::TransformationAddSynonym::COPY_OBJECT);
|
||||
});
|
||||
|
||||
// At this point, |relevant_instructions| contains all the instructions
|
||||
|
@ -84,6 +84,16 @@ void FuzzerPassDonateModules::Apply() {
|
||||
|
||||
void FuzzerPassDonateModules::DonateSingleModule(
|
||||
opt::IRContext* donor_ir_context, bool make_livesafe) {
|
||||
// Check that the donated module has capabilities, supported by the recipient
|
||||
// module.
|
||||
for (const auto& capability_inst : donor_ir_context->capabilities()) {
|
||||
auto capability =
|
||||
static_cast<SpvCapability>(capability_inst.GetSingleWordInOperand(0));
|
||||
if (!GetIRContext()->get_feature_mgr()->HasCapability(capability)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The ids used by the donor module may very well clash with ids defined in
|
||||
// the recipient module. Furthermore, some instructions defined in the donor
|
||||
// module will be equivalent to instructions defined in the recipient module,
|
||||
@ -646,12 +656,13 @@ void FuzzerPassDonateModules::HandleFunctions(
|
||||
}
|
||||
});
|
||||
|
||||
if (make_livesafe) {
|
||||
// Make the function livesafe and then add it.
|
||||
AddLivesafeFunction(*function_to_donate, donor_ir_context,
|
||||
*original_id_to_donated_id, donated_instructions);
|
||||
} else {
|
||||
// Add the function in a non-livesafe manner.
|
||||
// If |make_livesafe| is true, try to add the function in a livesafe manner.
|
||||
// Otherwise (if |make_lifesafe| is false or an attempt to make the function
|
||||
// livesafe has failed), add the function in a non-livesafe manner.
|
||||
if (!make_livesafe ||
|
||||
!MaybeAddLivesafeFunction(*function_to_donate, donor_ir_context,
|
||||
*original_id_to_donated_id,
|
||||
donated_instructions)) {
|
||||
ApplyTransformation(TransformationAddFunction(donated_instructions));
|
||||
}
|
||||
}
|
||||
@ -773,6 +784,7 @@ bool FuzzerPassDonateModules::IsBasicType(
|
||||
const opt::Instruction& instruction) const {
|
||||
switch (instruction.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeMatrix:
|
||||
@ -1007,7 +1019,96 @@ void FuzzerPassDonateModules::PrepareInstructionForDonation(
|
||||
input_operands));
|
||||
}
|
||||
|
||||
void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
bool FuzzerPassDonateModules::CreateLoopLimiterInfo(
|
||||
opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header,
|
||||
const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
|
||||
protobufs::LoopLimiterInfo* out) {
|
||||
assert(loop_header.IsLoopHeader() && "|loop_header| is not a loop header");
|
||||
|
||||
// Grab the loop header's id, mapped to its donated value.
|
||||
out->set_loop_header_id(original_id_to_donated_id.at(loop_header.id()));
|
||||
|
||||
// Get fresh ids that will be used to load the loop limiter, increment
|
||||
// it, compare it with the loop limit, and an id for a new block that
|
||||
// will contain the loop's original terminator.
|
||||
out->set_load_id(GetFuzzerContext()->GetFreshId());
|
||||
out->set_increment_id(GetFuzzerContext()->GetFreshId());
|
||||
out->set_compare_id(GetFuzzerContext()->GetFreshId());
|
||||
out->set_logical_op_id(GetFuzzerContext()->GetFreshId());
|
||||
|
||||
// We are creating a branch from the back-edge block to the merge block. Thus,
|
||||
// if merge block has any OpPhi instructions, we might need to adjust
|
||||
// them.
|
||||
|
||||
// Note that the loop might have an unreachable back-edge block. This means
|
||||
// that the loop can't iterate, so we don't need to adjust anything.
|
||||
const auto back_edge_block_id = TransformationAddFunction::GetBackEdgeBlockId(
|
||||
donor_ir_context, loop_header.id());
|
||||
if (!back_edge_block_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* back_edge_block = donor_ir_context->cfg()->block(back_edge_block_id);
|
||||
assert(back_edge_block && "|back_edge_block_id| is invalid");
|
||||
|
||||
const auto* merge_block =
|
||||
donor_ir_context->cfg()->block(loop_header.MergeBlockId());
|
||||
assert(merge_block && "Loop header has invalid merge block id");
|
||||
|
||||
// We don't need to adjust anything if there is already a branch from
|
||||
// the back-edge block to the merge block.
|
||||
if (back_edge_block->IsSuccessor(merge_block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adjust OpPhi instructions in the |merge_block|.
|
||||
for (const auto& inst : *merge_block) {
|
||||
if (inst.opcode() != SpvOpPhi) {
|
||||
break;
|
||||
}
|
||||
|
||||
// There is no simple way to ensure that a chosen operand for the OpPhi
|
||||
// instruction will never cause any problems (e.g. if we choose an
|
||||
// integer id, it might have a zero value when we branch from the back
|
||||
// edge block. This might cause a division by 0 later in the function.).
|
||||
// Thus, we ignore possible problems and proceed as follows:
|
||||
// - if any of the existing OpPhi operands dominates the back-edge
|
||||
// block - use it
|
||||
// - if OpPhi has a basic type (see IsBasicType method) - create
|
||||
// a zero constant
|
||||
// - otherwise, we can't add a livesafe function.
|
||||
uint32_t suitable_operand_id = 0;
|
||||
for (uint32_t i = 0; i < inst.NumInOperands(); i += 2) {
|
||||
auto dependency_inst_id = inst.GetSingleWordInOperand(i);
|
||||
|
||||
if (fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
donor_ir_context, back_edge_block->terminator(),
|
||||
dependency_inst_id)) {
|
||||
suitable_operand_id = original_id_to_donated_id.at(dependency_inst_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (suitable_operand_id == 0 &&
|
||||
IsBasicType(
|
||||
*donor_ir_context->get_def_use_mgr()->GetDef(inst.type_id()))) {
|
||||
// We mark this constant as irrelevant so that we can replace it
|
||||
// with more interesting value later.
|
||||
suitable_operand_id = FindOrCreateZeroConstant(
|
||||
original_id_to_donated_id.at(inst.type_id()), true);
|
||||
}
|
||||
|
||||
if (suitable_operand_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out->add_phi_id(suitable_operand_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FuzzerPassDonateModules::MaybeAddLivesafeFunction(
|
||||
const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
|
||||
const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
|
||||
const std::vector<protobufs::Instruction>& donated_instructions) {
|
||||
@ -1035,16 +1136,13 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
for (auto& block : function_to_donate) {
|
||||
if (block.IsLoopHeader()) {
|
||||
protobufs::LoopLimiterInfo loop_limiter;
|
||||
// Grab the loop header's id, mapped to its donated value.
|
||||
loop_limiter.set_loop_header_id(original_id_to_donated_id.at(block.id()));
|
||||
// Get fresh ids that will be used to load the loop limiter, increment
|
||||
// it, compare it with the loop limit, and an id for a new block that
|
||||
// will contain the loop's original terminator.
|
||||
loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
|
||||
loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
|
||||
loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
|
||||
loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
|
||||
loop_limiters.emplace_back(loop_limiter);
|
||||
|
||||
if (!CreateLoopLimiterInfo(donor_ir_context, block,
|
||||
original_id_to_donated_id, &loop_limiter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
loop_limiters.emplace_back(std::move(loop_limiter));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,6 +1255,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
ApplyTransformation(TransformationAddFunction(
|
||||
donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
|
||||
kill_unreachable_return_value_id, access_chain_clamping_info));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -128,14 +128,24 @@ class FuzzerPassDonateModules : public FuzzerPass {
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id,
|
||||
std::vector<protobufs::Instruction>* donated_instructions);
|
||||
|
||||
// Tries to create a protobufs::LoopLimiterInfo given a loop header basic
|
||||
// block. Returns true if successful and outputs loop limiter into the |out|
|
||||
// variable. Otherwise, returns false. |out| contains an undefined value when
|
||||
// this function returns false.
|
||||
bool CreateLoopLimiterInfo(
|
||||
opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header,
|
||||
const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
|
||||
protobufs::LoopLimiterInfo* out);
|
||||
|
||||
// Requires that |donated_instructions| represents a prepared version of the
|
||||
// instructions of |function_to_donate| (which comes from |donor_ir_context|)
|
||||
// ready for donation, and |original_id_to_donated_id| maps ids from
|
||||
// |donor_ir_context| to their corresponding ids in the recipient module.
|
||||
//
|
||||
// Adds a livesafe version of the function, based on |donated_instructions|,
|
||||
// to the recipient module.
|
||||
void AddLivesafeFunction(
|
||||
// Attempts to add a livesafe version of the function, based on
|
||||
// |donated_instructions|, to the recipient module. Returns true if the
|
||||
// donation was successful, false otherwise.
|
||||
bool MaybeAddLivesafeFunction(
|
||||
const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
|
||||
const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
|
||||
const std::vector<protobufs::Instruction>& donated_instructions);
|
||||
|
@ -91,6 +91,13 @@ void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
|
||||
|
||||
uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
FindOrCreateToggledIntegerConstant(uint32_t id) {
|
||||
// |id| must not be a specialization constant because we do not know the value
|
||||
// of specialization constants.
|
||||
if (opt::IsSpecConstantInst(
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(id)->opcode())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
|
||||
|
||||
// This pass only toggles integer constants.
|
||||
|
@ -34,6 +34,12 @@ FuzzerPassInterchangeZeroLikeConstants::
|
||||
|
||||
uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
|
||||
opt::Instruction* declaration) {
|
||||
// |declaration| must not be a specialization constant because we do not know
|
||||
// the value of specialization constants.
|
||||
if (opt::IsSpecConstantInst(declaration->opcode())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
|
||||
declaration->result_id());
|
||||
|
||||
|
71
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
vendored
Normal file
71
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2020 André Perez Maselco
|
||||
//
|
||||
// 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_make_vector_operations_dynamic.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_make_vector_operation_dynamic.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassMakeVectorOperationsDynamic::
|
||||
~FuzzerPassMakeVectorOperationsDynamic() = default;
|
||||
|
||||
void FuzzerPassMakeVectorOperationsDynamic::Apply() {
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& instruction : block) {
|
||||
// Randomly decide whether to try applying the transformation.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfMakingVectorOperationDynamic())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// |instruction| must be a vector operation.
|
||||
if (!TransformationMakeVectorOperationDynamic::IsVectorOperation(
|
||||
GetIRContext(), &instruction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure |instruction| has only one indexing operand.
|
||||
assert(instruction.NumInOperands() ==
|
||||
(instruction.opcode() == SpvOpCompositeExtract ? 2 : 3) &&
|
||||
"FuzzerPassMakeVectorOperationsDynamic: the composite "
|
||||
"instruction must have "
|
||||
"only one indexing operand.");
|
||||
|
||||
// Applies the make vector operation dynamic transformation.
|
||||
ApplyTransformation(TransformationMakeVectorOperationDynamic(
|
||||
instruction.result_id(),
|
||||
FindOrCreateIntegerConstant(
|
||||
{instruction.GetSingleWordInOperand(
|
||||
instruction.opcode() == SpvOpCompositeExtract ? 1 : 2)},
|
||||
32, GetFuzzerContext()->ChooseEven(), false)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 André Perez Maselco
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Looks for OpCompositeExtract/Insert instructions on vectors, and replaces
|
||||
// them with OpVectorExtract/InsertDynamic.
|
||||
class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassMakeVectorOperationsDynamic(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassMakeVectorOperationsDynamic() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_
|
@ -47,32 +47,13 @@ void FuzzerPassOutlineFunctions::Apply() {
|
||||
for (auto& block : *function) {
|
||||
blocks.push_back(&block);
|
||||
}
|
||||
auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
|
||||
auto entry_block = MaybeGetEntryBlockSuitableForOutlining(
|
||||
blocks[GetFuzzerContext()->RandomIndex(blocks)]);
|
||||
|
||||
// If the entry block starts with OpPhi, try to split it.
|
||||
if (entry_block->begin()->opcode() == SpvOpPhi) {
|
||||
// Find the first non-OpPhi instruction.
|
||||
opt::Instruction* non_phi_inst = nullptr;
|
||||
for (auto& instruction : *entry_block) {
|
||||
if (instruction.opcode() != SpvOpPhi) {
|
||||
non_phi_inst = &instruction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(non_phi_inst && "|non_phi_inst| must've been initialized");
|
||||
|
||||
// If the split was not applicable, the transformation will not work.
|
||||
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
|
||||
if (!MaybeApplyTransformation(TransformationSplitBlock(
|
||||
MakeInstructionDescriptor(non_phi_inst->result_id(),
|
||||
non_phi_inst->opcode(), 0),
|
||||
new_block_id))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The new entry block is the newly-created block.
|
||||
entry_block = &*function->FindBlock(new_block_id);
|
||||
if (!entry_block) {
|
||||
// The chosen block is not suitable to be the entry block of a region that
|
||||
// will be outlined.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
|
||||
@ -83,16 +64,26 @@ void FuzzerPassOutlineFunctions::Apply() {
|
||||
postdominates_entry_block != nullptr;
|
||||
postdominates_entry_block = postdominator_analysis->ImmediateDominator(
|
||||
postdominates_entry_block)) {
|
||||
// Consider the block if it is dominated by the entry block, ignore it if
|
||||
// it is a continue target.
|
||||
if (dominator_analysis->Dominates(entry_block,
|
||||
postdominates_entry_block)) {
|
||||
postdominates_entry_block) &&
|
||||
!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
|
||||
postdominates_entry_block->id())) {
|
||||
candidate_exit_blocks.push_back(postdominates_entry_block);
|
||||
}
|
||||
}
|
||||
if (candidate_exit_blocks.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
|
||||
candidate_exit_blocks)];
|
||||
auto exit_block = MaybeGetExitBlockSuitableForOutlining(
|
||||
candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
|
||||
candidate_exit_blocks)]);
|
||||
|
||||
if (!exit_block) {
|
||||
// The block chosen is not suitable
|
||||
continue;
|
||||
}
|
||||
|
||||
auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
|
||||
GetIRContext(), entry_block, exit_block);
|
||||
@ -122,5 +113,84 @@ void FuzzerPassOutlineFunctions::Apply() {
|
||||
}
|
||||
}
|
||||
|
||||
opt::BasicBlock*
|
||||
FuzzerPassOutlineFunctions::MaybeGetEntryBlockSuitableForOutlining(
|
||||
opt::BasicBlock* entry_block) {
|
||||
// If the entry block is a loop header, we need to get or create its
|
||||
// preheader and make it the entry block, if possible.
|
||||
if (entry_block->IsLoopHeader()) {
|
||||
auto predecessors =
|
||||
GetIRContext()->cfg()->preds(entry_block->GetLabel()->result_id());
|
||||
|
||||
if (predecessors.size() < 2) {
|
||||
// The header only has one predecessor (the back-edge block) and thus
|
||||
// it is unreachable. The block cannot be adjusted to be suitable for
|
||||
// outlining.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get or create a suitable preheader and make it become the entry block.
|
||||
entry_block =
|
||||
GetOrCreateSimpleLoopPreheader(entry_block->GetLabel()->result_id());
|
||||
}
|
||||
|
||||
assert(!entry_block->IsLoopHeader() &&
|
||||
"The entry block cannot be a loop header at this point.");
|
||||
|
||||
// If the entry block starts with OpPhi or OpVariable, try to split it.
|
||||
if (entry_block->begin()->opcode() == SpvOpPhi ||
|
||||
entry_block->begin()->opcode() == SpvOpVariable) {
|
||||
// Find the first non-OpPhi and non-OpVariable instruction.
|
||||
auto non_phi_or_var_inst = &*entry_block->begin();
|
||||
while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
|
||||
non_phi_or_var_inst->opcode() == SpvOpVariable) {
|
||||
non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
|
||||
}
|
||||
|
||||
// Split the block.
|
||||
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationSplitBlock(
|
||||
MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst),
|
||||
new_block_id));
|
||||
|
||||
// The new entry block is the newly-created block.
|
||||
entry_block = &*entry_block->GetParent()->FindBlock(new_block_id);
|
||||
}
|
||||
|
||||
return entry_block;
|
||||
}
|
||||
|
||||
opt::BasicBlock*
|
||||
FuzzerPassOutlineFunctions::MaybeGetExitBlockSuitableForOutlining(
|
||||
opt::BasicBlock* exit_block) {
|
||||
// The exit block must not be a continue target.
|
||||
assert(!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
|
||||
exit_block->id()) &&
|
||||
"A candidate exit block cannot be a continue target.");
|
||||
|
||||
// If the exit block is a merge block, try to split it and return the second
|
||||
// block in the pair as the exit block.
|
||||
if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(
|
||||
exit_block->id())) {
|
||||
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// Find the first non-OpPhi instruction, after which to split.
|
||||
auto split_before = &*exit_block->begin();
|
||||
while (split_before->opcode() == SpvOpPhi) {
|
||||
split_before = split_before->NextNode();
|
||||
}
|
||||
|
||||
if (!MaybeApplyTransformation(TransformationSplitBlock(
|
||||
MakeInstructionDescriptor(GetIRContext(), split_before),
|
||||
new_block_id))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*exit_block->GetParent()->FindBlock(new_block_id);
|
||||
}
|
||||
|
||||
return exit_block;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -32,6 +32,31 @@ class FuzzerPassOutlineFunctions : public FuzzerPass {
|
||||
~FuzzerPassOutlineFunctions();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
// Returns a block suitable to be an entry block for a region that can be
|
||||
// outlined, i.e. a block that is not a loop header and that does not start
|
||||
// with OpPhi or OpVariable. In particular, it returns:
|
||||
// - |entry_block| if it is suitable
|
||||
// - otherwise, a block found by:
|
||||
// - looking for or creating a new preheader, if |entry_block| is a loop
|
||||
// header
|
||||
// - splitting the candidate entry block, if it starts with OpPhi or
|
||||
// OpVariable.
|
||||
// Returns nullptr if a suitable block cannot be found following the
|
||||
// instructions above.
|
||||
opt::BasicBlock* MaybeGetEntryBlockSuitableForOutlining(
|
||||
opt::BasicBlock* entry_block);
|
||||
|
||||
// Returns:
|
||||
// - |exit_block| if it is not a merge block
|
||||
// - the second block obtained by splitting |exit_block|, if |exit_block| is a
|
||||
// merge block.
|
||||
// Assumes that |exit_block| is not a continue target.
|
||||
// The block returned by this function should be suitable to be the exit block
|
||||
// of a region that can be outlined.
|
||||
// Returns nullptr if |exit_block| is a merge block and it cannot be split.
|
||||
opt::BasicBlock* MaybeGetExitBlockSuitableForOutlining(
|
||||
opt::BasicBlock* exit_block);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp
vendored
Normal file
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_permute_instructions.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_move_instruction_down.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default;
|
||||
|
||||
void FuzzerPassPermuteInstructions::Apply() {
|
||||
// We are iterating over all instructions in all basic blocks.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// We need to collect all instructions in the block into a separate vector
|
||||
// since application of the transformation below might invalidate
|
||||
// iterators.
|
||||
std::vector<opt::Instruction*> instructions;
|
||||
for (auto& instruction : block) {
|
||||
instructions.push_back(&instruction);
|
||||
}
|
||||
|
||||
// We consider all instructions in reverse to increase the possible number
|
||||
// of applied transformations.
|
||||
for (auto it = instructions.rbegin(); it != instructions.rend(); ++it) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfPermutingInstructions())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (MaybeApplyTransformation(TransformationMoveInstructionDown(
|
||||
MakeInstructionDescriptor(GetIRContext(), *it)))) {
|
||||
// Apply the transformation as many times as possible.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Permutes instructions in every block of all while preserving the module's
|
||||
// semantics.
|
||||
class FuzzerPassPermuteInstructions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassPermuteInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassPermuteInstructions() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_
|
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
vendored
Normal file
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_propagate_instruction_up.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassPropagateInstructionsUp::~FuzzerPassPropagateInstructionsUp() =
|
||||
default;
|
||||
|
||||
void FuzzerPassPropagateInstructionsUp::Apply() {
|
||||
for (const auto& function : *GetIRContext()->module()) {
|
||||
for (const auto& block : function) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfPropagatingInstructionsUp())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TransformationPropagateInstructionUp::IsApplicableToBlock(
|
||||
GetIRContext(), block.id())) {
|
||||
std::map<uint32_t, uint32_t> fresh_ids;
|
||||
for (auto id : GetIRContext()->cfg()->preds(block.id())) {
|
||||
auto& fresh_id = fresh_ids[id];
|
||||
|
||||
if (!fresh_id) {
|
||||
// Create a fresh id if it hasn't been created yet. |fresh_id| will
|
||||
// be default-initialized to 0 in this case.
|
||||
fresh_id = GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTransformation(
|
||||
TransformationPropagateInstructionUp(block.id(), fresh_ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Decides whether to propagate instructions from some block into its
|
||||
// predecessors.
|
||||
class FuzzerPassPropagateInstructionsUp : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassPropagateInstructionsUp(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassPropagateInstructionsUp() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_
|
76
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
vendored
Normal file
76
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
// 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_replace_adds_subs_muls_with_carrying_extended.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
|
||||
} // namespace
|
||||
|
||||
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
|
||||
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
|
||||
~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
|
||||
|
||||
void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& instruction : block) {
|
||||
// Randomly decide whether to apply the transformation.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfReplacingAddSubMulWithCarryingExtended())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the transformation can be applied to this instruction.
|
||||
if (!TransformationReplaceAddSubMulWithCarryingExtended::
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
42
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
vendored
Normal file
42
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// 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_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of
|
||||
// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended,
|
||||
// OpSMulExtended) computes the result into a struct. The second one extracts
|
||||
// the appropriate component from the struct to yield the original result.
|
||||
class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
|
@ -36,23 +36,27 @@ FuzzerPassReplaceLinearAlgebraInstructions::
|
||||
void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
|
||||
// For each instruction, checks whether it is a linear algebra instruction. In
|
||||
// this case, the transformation is randomly applied.
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
|
||||
return;
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& instruction : block) {
|
||||
if (!spvOpcodeIsLinearAlgebra(instruction.opcode())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfReplacingLinearAlgebraInstructions())) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationReplaceLinearAlgebraInstruction(
|
||||
GetFuzzerContext()->GetFreshIds(
|
||||
TransformationReplaceLinearAlgebraInstruction::
|
||||
GetRequiredFreshIdCount(GetIRContext(), instruction)),
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction)));
|
||||
});
|
||||
GetRequiredFreshIdCount(GetIRContext(), &instruction)),
|
||||
MakeInstructionDescriptor(GetIRContext(), &instruction)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
38
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
38
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@ -826,6 +826,12 @@ uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
|
||||
operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
|
||||
parameter_type_ids.end());
|
||||
|
||||
// A trivial case - we change nothing.
|
||||
if (FindFunctionType(ir_context, operand_ids) ==
|
||||
old_function_type->result_id()) {
|
||||
return old_function_type->result_id();
|
||||
}
|
||||
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
|
||||
FindFunctionType(ir_context, operand_ids) == 0) {
|
||||
// We can change |old_function_type| only if it's used once in the module
|
||||
@ -849,17 +855,16 @@ uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
|
||||
// existing one or create a new one.
|
||||
auto type_id = FindOrCreateFunctionType(
|
||||
ir_context, new_function_type_result_id, operand_ids);
|
||||
assert(type_id != old_function_type->result_id() &&
|
||||
"We should've handled this case above");
|
||||
|
||||
if (type_id != old_function_type->result_id()) {
|
||||
function->DefInst().SetInOperand(1, {type_id});
|
||||
|
||||
// DefUseManager hasn't been updated yet, so if the following condition is
|
||||
// true, then |old_function_type| will have no users when this function
|
||||
// returns. We might as well remove it.
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
||||
old_function_type->RemoveFromList();
|
||||
delete old_function_type;
|
||||
}
|
||||
ir_context->KillInst(old_function_type);
|
||||
}
|
||||
|
||||
return type_id;
|
||||
@ -1312,6 +1317,31 @@ MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data) {
|
||||
return result;
|
||||
}
|
||||
|
||||
opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
|
||||
uint32_t block_id,
|
||||
SpvOp opcode) {
|
||||
// CFG::block uses std::map::at which throws an exception when |block_id| is
|
||||
// invalid. The error message is unhelpful, though. Thus, we test that
|
||||
// |block_id| is valid here.
|
||||
const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
|
||||
(void)label_inst; // Make compilers happy in release mode.
|
||||
assert(label_inst && label_inst->opcode() == SpvOpLabel &&
|
||||
"|block_id| is invalid");
|
||||
|
||||
auto* block = ir_context->cfg()->block(block_id);
|
||||
auto it = block->rbegin();
|
||||
assert(it != block->rend() && "Basic block can't be empty");
|
||||
|
||||
if (block->GetMergeInst()) {
|
||||
++it;
|
||||
assert(it != block->rend() &&
|
||||
"|block| must have at least two instructions:"
|
||||
"terminator and a merge instruction");
|
||||
}
|
||||
|
||||
return CanInsertOpcodeBeforeInstruction(opcode, &*it) ? &*it : nullptr;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
10
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
10
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@ -316,6 +316,10 @@ opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||
// more users, it is removed from the module. Returns the result id of the
|
||||
// OpTypeFunction instruction that is used as a type of the function with
|
||||
// |function_id|.
|
||||
//
|
||||
// CAUTION: When the old type of the function is removed from the module, its
|
||||
// memory is deallocated. Be sure not to use any pointers to the old
|
||||
// type when this function returns.
|
||||
uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
|
||||
uint32_t new_function_type_result_id,
|
||||
uint32_t return_type_id,
|
||||
@ -483,6 +487,12 @@ std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
|
||||
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
|
||||
MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
|
||||
|
||||
// Returns the last instruction in |block_id| before which an instruction with
|
||||
// opcode |opcode| can be inserted, or nullptr if there is no such instruction.
|
||||
opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
|
||||
uint32_t block_id,
|
||||
SpvOp opcode);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -52,7 +52,7 @@ protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
|
||||
opt::IRContext* context, opt::Instruction* inst,
|
||||
uint32_t in_operand_index) {
|
||||
const auto& in_operand = inst->GetInOperand(in_operand_index);
|
||||
assert(in_operand.type == SPV_OPERAND_TYPE_ID);
|
||||
assert(spvIsInIdType(in_operand.type));
|
||||
return MakeIdUseDescriptor(in_operand.words[0],
|
||||
MakeInstructionDescriptor(context, inst),
|
||||
in_operand_index);
|
||||
|
@ -408,6 +408,10 @@ message Transformation {
|
||||
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
|
||||
TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
|
||||
TransformationAddLoopPreheader add_loop_preheader = 63;
|
||||
TransformationMoveInstructionDown move_instruction_down = 64;
|
||||
TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
|
||||
TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
|
||||
TransformationPropagateInstructionUp propagate_instruction_up = 67;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@ -1105,6 +1109,21 @@ message TransformationLoad {
|
||||
|
||||
}
|
||||
|
||||
message TransformationMakeVectorOperationDynamic {
|
||||
|
||||
// A transformation that replaces the OpCompositeExtract and OpCompositeInsert
|
||||
// instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
|
||||
|
||||
// The composite instruction result id.
|
||||
uint32 instruction_result_id = 1;
|
||||
|
||||
// The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object.
|
||||
// However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction.
|
||||
// This is the result id of the integer instruction.
|
||||
uint32 constant_index_id = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationMergeBlocks {
|
||||
|
||||
// A transformation that merges a block with its predecessor.
|
||||
@ -1124,6 +1143,15 @@ message TransformationMoveBlockDown {
|
||||
uint32 block_id = 1;
|
||||
}
|
||||
|
||||
message TransformationMoveInstructionDown {
|
||||
|
||||
// Swaps |instruction| with the next instruction in the block.
|
||||
|
||||
// The instruction to move down.
|
||||
InstructionDescriptor instruction = 1;
|
||||
|
||||
}
|
||||
|
||||
message TransformationOutlineFunction {
|
||||
|
||||
// A transformation that outlines a single-entry single-exit region of a
|
||||
@ -1212,6 +1240,26 @@ message TransformationPermutePhiOperands {
|
||||
|
||||
}
|
||||
|
||||
message TransformationPropagateInstructionUp {
|
||||
|
||||
// Propagates an instruction in the block into the block's predecessors.
|
||||
// Concretely, this transformation clones some particular instruction from
|
||||
// the |block_id| into every block's predecessor and replaces the original
|
||||
// instruction with OpPhi. Take a look at the transformation class to learn
|
||||
// more about how we choose what instruction to propagate.
|
||||
|
||||
// Id of the block to propagate an instruction from.
|
||||
uint32 block_id = 1;
|
||||
|
||||
// A map from the id of some predecessor of the |block_id| to the fresh id.
|
||||
// The map contains a fresh id for at least every predecessor of the |block_id|.
|
||||
// The instruction is propagated by creating a number of clones - one clone for
|
||||
// each predecessor. Fresh ids from this field are used as result ids of cloned
|
||||
// instructions.
|
||||
repeated UInt32Pair predecessor_id_to_fresh_id = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationPushIdThroughVariable {
|
||||
|
||||
// A transformation that makes |value_synonym_id| and |value_id| to be
|
||||
@ -1259,6 +1307,24 @@ message TransformationRecordSynonymousConstants {
|
||||
|
||||
}
|
||||
|
||||
message TransformationReplaceAddSubMulWithCarryingExtended {
|
||||
|
||||
// Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
|
||||
// with OpUMulExtended or OpSMulExtended (depending on the signedness
|
||||
// of the operands) and stores the result into a |struct_fresh_id|.
|
||||
// In the original instruction the result type id and the type ids of
|
||||
// the operands must be the same. Then the transformation extracts
|
||||
// the first element of the result into the original |result_id|.
|
||||
// This value is the same as the result of the original instruction.
|
||||
|
||||
// The fresh id of the intermediate result.
|
||||
uint32 struct_fresh_id = 1;
|
||||
|
||||
// The result id of the original instruction.
|
||||
uint32 result_id = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationReplaceParameterWithGlobal {
|
||||
|
||||
// Removes parameter with result id |parameter_id| from its function
|
||||
|
@ -54,13 +54,17 @@
|
||||
#include "source/fuzz/transformation_function_call.h"
|
||||
#include "source/fuzz/transformation_invert_comparison_operator.h"
|
||||
#include "source/fuzz/transformation_load.h"
|
||||
#include "source/fuzz/transformation_make_vector_operation_dynamic.h"
|
||||
#include "source/fuzz/transformation_merge_blocks.h"
|
||||
#include "source/fuzz/transformation_move_block_down.h"
|
||||
#include "source/fuzz/transformation_move_instruction_down.h"
|
||||
#include "source/fuzz/transformation_outline_function.h"
|
||||
#include "source/fuzz/transformation_permute_function_parameters.h"
|
||||
#include "source/fuzz/transformation_permute_phi_operands.h"
|
||||
#include "source/fuzz/transformation_propagate_instruction_up.h"
|
||||
#include "source/fuzz/transformation_push_id_through_variable.h"
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
|
||||
@ -190,10 +194,17 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
message.invert_comparison_operator());
|
||||
case protobufs::Transformation::TransformationCase::kLoad:
|
||||
return MakeUnique<TransformationLoad>(message.load());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kMakeVectorOperationDynamic:
|
||||
return MakeUnique<TransformationMakeVectorOperationDynamic>(
|
||||
message.make_vector_operation_dynamic());
|
||||
case protobufs::Transformation::TransformationCase::kMergeBlocks:
|
||||
return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
|
||||
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
|
||||
return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
|
||||
case protobufs::Transformation::TransformationCase::kMoveInstructionDown:
|
||||
return MakeUnique<TransformationMoveInstructionDown>(
|
||||
message.move_instruction_down());
|
||||
case protobufs::Transformation::TransformationCase::kOutlineFunction:
|
||||
return MakeUnique<TransformationOutlineFunction>(
|
||||
message.outline_function());
|
||||
@ -204,6 +215,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
|
||||
return MakeUnique<TransformationPermutePhiOperands>(
|
||||
message.permute_phi_operands());
|
||||
case protobufs::Transformation::TransformationCase::kPropagateInstructionUp:
|
||||
return MakeUnique<TransformationPropagateInstructionUp>(
|
||||
message.propagate_instruction_up());
|
||||
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
|
||||
return MakeUnique<TransformationPushIdThroughVariable>(
|
||||
message.push_id_through_variable());
|
||||
@ -212,9 +226,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
return MakeUnique<TransformationRecordSynonymousConstants>(
|
||||
message.record_synonymous_constants());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceParameterWithGlobal:
|
||||
return MakeUnique<TransformationReplaceParameterWithGlobal>(
|
||||
message.replace_parameter_with_global());
|
||||
kReplaceAddSubMulWithCarryingExtended:
|
||||
return MakeUnique<TransformationReplaceAddSubMulWithCarryingExtended>(
|
||||
message.replace_add_sub_mul_with_carrying_extended());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceBooleanConstantWithConstantBinary:
|
||||
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
|
||||
@ -242,6 +256,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kReplaceLoadStoreWithCopyMemory:
|
||||
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
|
||||
message.replace_load_store_with_copy_memory());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceParameterWithGlobal:
|
||||
return MakeUnique<TransformationReplaceParameterWithGlobal>(
|
||||
message.replace_parameter_with_global());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceParamsWithStruct:
|
||||
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
||||
|
@ -363,6 +363,22 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe(
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t TransformationAddFunction::GetBackEdgeBlockId(
|
||||
opt::IRContext* ir_context, uint32_t loop_header_block_id) {
|
||||
const auto* loop_header_block =
|
||||
ir_context->cfg()->block(loop_header_block_id);
|
||||
assert(loop_header_block && "|loop_header_block_id| is invalid");
|
||||
|
||||
for (auto pred : ir_context->cfg()->preds(loop_header_block_id)) {
|
||||
if (ir_context->GetDominatorAnalysis(loop_header_block->GetParent())
|
||||
->Dominates(loop_header_block_id, pred)) {
|
||||
return pred;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
opt::IRContext* ir_context, opt::Function* added_function) const {
|
||||
// Collect up all the loop headers so that we can subsequently add loop
|
||||
@ -474,14 +490,8 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
for (auto loop_header : loop_headers) {
|
||||
// Look for the loop's back-edge block. This is a predecessor of the loop
|
||||
// header that is dominated by the loop header.
|
||||
uint32_t back_edge_block_id = 0;
|
||||
for (auto pred : ir_context->cfg()->preds(loop_header->id())) {
|
||||
if (ir_context->GetDominatorAnalysis(added_function)
|
||||
->Dominates(loop_header->id(), pred)) {
|
||||
back_edge_block_id = pred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto back_edge_block_id =
|
||||
GetBackEdgeBlockId(ir_context, loop_header->id());
|
||||
if (!back_edge_block_id) {
|
||||
// The loop's back-edge block must be unreachable. This means that the
|
||||
// loop cannot iterate, so there is no need to make it lifesafe; we can
|
||||
@ -692,9 +702,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
|
||||
back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}
|
||||
|
||||
},
|
||||
{SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}},
|
||||
{SPV_OPERAND_TYPE_ID, {loop_header->id()}}}));
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,12 @@ class TransformationAddFunction : public Transformation {
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
|
||||
uint32_t index_id);
|
||||
|
||||
// Returns id of the back-edge block, given the corresponding
|
||||
// |loop_header_block_id|. |loop_header_block_id| must be the id of a loop
|
||||
// header block. Returns 0 if the loop has no back-edge block.
|
||||
static uint32_t GetBackEdgeBlockId(opt::IRContext* ir_context,
|
||||
uint32_t loop_header_block_id);
|
||||
|
||||
private:
|
||||
// Attempts to create a function from the series of instructions in
|
||||
// |message_.instruction| and add it to |ir_context|.
|
||||
|
@ -14,8 +14,6 @@
|
||||
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
|
||||
#include <source/spirv_constant.h>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -72,13 +70,13 @@ void TransformationAddParameter::Apply(
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
auto parameter_type_id =
|
||||
const auto new_parameter_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
|
||||
assert(parameter_type_id != 0 && "Initializer has invalid type");
|
||||
assert(new_parameter_type_id != 0 && "Initializer has invalid type");
|
||||
|
||||
// Add new parameters to the function.
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFunctionParameter, parameter_type_id,
|
||||
ir_context, SpvOpFunctionParameter, new_parameter_type_id,
|
||||
message_.parameter_fresh_id(), opt::Instruction::OperandList()));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
|
||||
@ -92,43 +90,30 @@ void TransformationAddParameter::Apply(
|
||||
message_.parameter_fresh_id());
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(), [this](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != message_.function_id()) {
|
||||
return;
|
||||
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
}
|
||||
|
||||
call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
});
|
||||
// Update function's type.
|
||||
{
|
||||
// We use a separate scope here since |old_function_type| might become a
|
||||
// dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
|
||||
|
||||
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
const auto* old_function_type =
|
||||
fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function must have a valid type");
|
||||
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
||||
// Adjust existing function type if it is used only by this function.
|
||||
old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}});
|
||||
|
||||
// We must make sure that all dependencies of |old_function_type| are
|
||||
// evaluated before |old_function_type| (i.e. the domination rules are not
|
||||
// broken). Thus, we move |old_function_type| to the end of the list of all
|
||||
// types in the module.
|
||||
old_function_type->RemoveFromList();
|
||||
ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
|
||||
} else {
|
||||
// Otherwise, either create a new type or use an existing one.
|
||||
std::vector<uint32_t> type_ids;
|
||||
type_ids.reserve(old_function_type->NumInOperands() + 1);
|
||||
|
||||
for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) {
|
||||
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
|
||||
std::vector<uint32_t> parameter_type_ids;
|
||||
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
|
||||
parameter_type_ids.push_back(
|
||||
old_function_type->GetSingleWordInOperand(i));
|
||||
}
|
||||
|
||||
type_ids.push_back(parameter_type_id);
|
||||
parameter_type_ids.push_back(new_parameter_type_id);
|
||||
|
||||
function->DefInst().SetInOperand(
|
||||
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||
ir_context, message_.function_type_fresh_id(), type_ids)});
|
||||
fuzzerutil::UpdateFunctionType(
|
||||
ir_context, function->result_id(), message_.function_type_fresh_id(),
|
||||
old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
|
||||
}
|
||||
|
||||
// Make sure our changes are analyzed.
|
||||
|
@ -36,6 +36,28 @@ bool TransformationAddTypeFloat::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks float type width capabilities.
|
||||
switch (message_.width()) {
|
||||
case 16:
|
||||
// The Float16 capability must be present.
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat16)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
// No capabilities needed.
|
||||
break;
|
||||
case 64:
|
||||
// The Float64 capability must be present.
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat64)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unexpected float type width");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Applicable if there is no float type with this width already declared in
|
||||
// the module.
|
||||
return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0;
|
||||
|
@ -38,6 +38,34 @@ bool TransformationAddTypeInt::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks integer type width capabilities.
|
||||
switch (message_.width()) {
|
||||
case 8:
|
||||
// The Int8 capability must be present.
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt8)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
// The Int16 capability must be present.
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
// No capabilities needed.
|
||||
break;
|
||||
case 64:
|
||||
// The Int64 capability must be present.
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unexpected integer type width");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Applicable if there is no int type with this width and signedness already
|
||||
// declared in the module.
|
||||
return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(),
|
||||
|
111
3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp
vendored
Normal file
111
3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2020 André Perez Maselco
|
||||
//
|
||||
// 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_make_vector_operation_dynamic.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationMakeVectorOperationDynamic::
|
||||
TransformationMakeVectorOperationDynamic(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationMakeVectorOperationDynamic& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationMakeVectorOperationDynamic::
|
||||
TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
|
||||
uint32_t constant_index_id) {
|
||||
message_.set_instruction_result_id(instruction_result_id);
|
||||
message_.set_constant_index_id(constant_index_id);
|
||||
}
|
||||
|
||||
bool TransformationMakeVectorOperationDynamic::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |instruction| must be a vector operation.
|
||||
auto instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
|
||||
if (!IsVectorOperation(ir_context, instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |constant_index_instruction| must be defined as an integer instruction.
|
||||
auto constant_index_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.constant_index_id());
|
||||
if (!constant_index_instruction || !constant_index_instruction->type_id() ||
|
||||
!ir_context->get_type_mgr()
|
||||
->GetType(constant_index_instruction->type_id())
|
||||
->AsInteger()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationMakeVectorOperationDynamic::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
|
||||
|
||||
// The OpVectorInsertDynamic instruction has the vector and component operands
|
||||
// in reverse order in relation to the OpCompositeInsert corresponding
|
||||
// operands.
|
||||
if (instruction->opcode() == SpvOpCompositeInsert) {
|
||||
std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
|
||||
}
|
||||
|
||||
// Sets the literal operand to the equivalent constant.
|
||||
instruction->SetInOperand(
|
||||
instruction->opcode() == SpvOpCompositeExtract ? 1 : 2,
|
||||
{message_.constant_index_id()});
|
||||
|
||||
// Sets the |instruction| opcode to the corresponding vector dynamic opcode.
|
||||
instruction->SetOpcode(instruction->opcode() == SpvOpCompositeExtract
|
||||
? SpvOpVectorExtractDynamic
|
||||
: SpvOpVectorInsertDynamic);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationMakeVectorOperationDynamic::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_make_vector_operation_dynamic() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationMakeVectorOperationDynamic::IsVectorOperation(
|
||||
opt::IRContext* ir_context, opt::Instruction* instruction) {
|
||||
// |instruction| must be defined and must be an OpCompositeExtract/Insert
|
||||
// instruction.
|
||||
if (!instruction || (instruction->opcode() != SpvOpCompositeExtract &&
|
||||
instruction->opcode() != SpvOpCompositeInsert)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The composite must be a vector.
|
||||
auto composite_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(instruction->GetSingleWordInOperand(
|
||||
instruction->opcode() == SpvOpCompositeExtract ? 0 : 1));
|
||||
if (!ir_context->get_type_mgr()
|
||||
->GetType(composite_instruction->type_id())
|
||||
->AsVector()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
63
3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h
vendored
Normal file
63
3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2020 André Perez Maselco
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_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 TransformationMakeVectorOperationDynamic : public Transformation {
|
||||
public:
|
||||
explicit TransformationMakeVectorOperationDynamic(
|
||||
const protobufs::TransformationMakeVectorOperationDynamic& message);
|
||||
|
||||
TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
|
||||
uint32_t constant_index_id);
|
||||
|
||||
// - |message_.instruction_result_id| must be the result id of an
|
||||
// OpCompositeExtract/Insert instruction such that the composite operand is a
|
||||
// vector.
|
||||
// - |message_.constant_index_id| must be the result id of an integer
|
||||
// instruction such that its value equals the indexing literal of the
|
||||
// OpCompositeExtract/Insert instruction.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Replaces the OpCompositeExtract and OpCompositeInsert instructions with the
|
||||
// OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Checks |instruction| is defined, is an OpCompositeExtract/Insert
|
||||
// instruction and the composite operand is a vector.
|
||||
static bool IsVectorOperation(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction);
|
||||
|
||||
private:
|
||||
protobufs::TransformationMakeVectorOperationDynamic message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_
|
219
3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp
vendored
Normal file
219
3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_move_instruction_down.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationMoveInstructionDown::TransformationMoveInstructionDown(
|
||||
const protobufs::TransformationMoveInstructionDown& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationMoveInstructionDown::TransformationMoveInstructionDown(
|
||||
const protobufs::InstructionDescriptor& instruction) {
|
||||
*message_.mutable_instruction() = instruction;
|
||||
}
|
||||
|
||||
bool TransformationMoveInstructionDown::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |instruction| must be valid.
|
||||
auto* inst = FindInstruction(message_.instruction(), ir_context);
|
||||
if (!inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Instruction's opcode must be supported by this transformation.
|
||||
if (!IsOpcodeSupported(inst->opcode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* inst_block = ir_context->get_instr_block(inst);
|
||||
assert(inst_block &&
|
||||
"Global instructions and function parameters are not supported");
|
||||
|
||||
auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst);
|
||||
assert(inst_it != inst_block->end() &&
|
||||
"Can't get an iterator for the instruction");
|
||||
|
||||
// |instruction| can't be the last instruction in the block.
|
||||
auto successor_it = ++inst_it;
|
||||
if (successor_it == inst_block->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we can insert |instruction| after |inst_it|.
|
||||
auto successors_successor_it = ++inst_it;
|
||||
if (successors_successor_it == inst_block->end() ||
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(),
|
||||
successors_successor_it)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |instruction|'s successor doesn't depend on the |instruction|.
|
||||
if (inst->result_id()) {
|
||||
for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) {
|
||||
const auto& operand = successor_it->GetInOperand(i);
|
||||
if (operand.type == SPV_OPERAND_TYPE_ID &&
|
||||
operand.words[0] == inst->result_id()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationMoveInstructionDown::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto* inst = FindInstruction(message_.instruction(), ir_context);
|
||||
assert(inst &&
|
||||
"The instruction should've been validated in the IsApplicable");
|
||||
|
||||
auto inst_it = fuzzerutil::GetIteratorForInstruction(
|
||||
ir_context->get_instr_block(inst), inst);
|
||||
|
||||
// Move the instruction down in the block.
|
||||
inst->InsertAfter(&*++inst_it);
|
||||
|
||||
ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_move_instruction_down() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationMoveInstructionDown::IsOpcodeSupported(SpvOp opcode) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
|
||||
// We only support "simple" instructions that work don't with memory.
|
||||
// We should extend this so that we support the ones that modify the memory
|
||||
// too.
|
||||
switch (opcode) {
|
||||
case SpvOpNop:
|
||||
case SpvOpUndef:
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
case SpvOpArrayLength:
|
||||
case SpvOpVectorExtractDynamic:
|
||||
case SpvOpVectorInsertDynamic:
|
||||
case SpvOpVectorShuffle:
|
||||
case SpvOpCompositeConstruct:
|
||||
case SpvOpCompositeExtract:
|
||||
case SpvOpCompositeInsert:
|
||||
case SpvOpCopyObject:
|
||||
case SpvOpTranspose:
|
||||
case SpvOpConvertFToU:
|
||||
case SpvOpConvertFToS:
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
case SpvOpUConvert:
|
||||
case SpvOpSConvert:
|
||||
case SpvOpFConvert:
|
||||
case SpvOpQuantizeToF16:
|
||||
case SpvOpSatConvertSToU:
|
||||
case SpvOpSatConvertUToS:
|
||||
case SpvOpBitcast:
|
||||
case SpvOpSNegate:
|
||||
case SpvOpFNegate:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpFAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpFSub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpFMul:
|
||||
case SpvOpUDiv:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpFDiv:
|
||||
case SpvOpUMod:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpFRem:
|
||||
case SpvOpFMod:
|
||||
case SpvOpVectorTimesScalar:
|
||||
case SpvOpMatrixTimesScalar:
|
||||
case SpvOpVectorTimesMatrix:
|
||||
case SpvOpMatrixTimesVector:
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
case SpvOpOuterProduct:
|
||||
case SpvOpDot:
|
||||
case SpvOpIAddCarry:
|
||||
case SpvOpISubBorrow:
|
||||
case SpvOpUMulExtended:
|
||||
case SpvOpSMulExtended:
|
||||
case SpvOpAny:
|
||||
case SpvOpAll:
|
||||
case SpvOpIsNan:
|
||||
case SpvOpIsInf:
|
||||
case SpvOpIsFinite:
|
||||
case SpvOpIsNormal:
|
||||
case SpvOpSignBitSet:
|
||||
case SpvOpLessOrGreater:
|
||||
case SpvOpOrdered:
|
||||
case SpvOpUnordered:
|
||||
case SpvOpLogicalEqual:
|
||||
case SpvOpLogicalNotEqual:
|
||||
case SpvOpLogicalOr:
|
||||
case SpvOpLogicalAnd:
|
||||
case SpvOpLogicalNot:
|
||||
case SpvOpSelect:
|
||||
case SpvOpIEqual:
|
||||
case SpvOpINotEqual:
|
||||
case SpvOpUGreaterThan:
|
||||
case SpvOpSGreaterThan:
|
||||
case SpvOpUGreaterThanEqual:
|
||||
case SpvOpSGreaterThanEqual:
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan:
|
||||
case SpvOpULessThanEqual:
|
||||
case SpvOpSLessThanEqual:
|
||||
case SpvOpFOrdEqual:
|
||||
case SpvOpFUnordEqual:
|
||||
case SpvOpFOrdNotEqual:
|
||||
case SpvOpFUnordNotEqual:
|
||||
case SpvOpFOrdLessThan:
|
||||
case SpvOpFUnordLessThan:
|
||||
case SpvOpFOrdGreaterThan:
|
||||
case SpvOpFUnordGreaterThan:
|
||||
case SpvOpFOrdLessThanEqual:
|
||||
case SpvOpFUnordLessThanEqual:
|
||||
case SpvOpFOrdGreaterThanEqual:
|
||||
case SpvOpFUnordGreaterThanEqual:
|
||||
case SpvOpShiftRightLogical:
|
||||
case SpvOpShiftRightArithmetic:
|
||||
case SpvOpShiftLeftLogical:
|
||||
case SpvOpBitwiseOr:
|
||||
case SpvOpBitwiseXor:
|
||||
case SpvOpBitwiseAnd:
|
||||
case SpvOpNot:
|
||||
case SpvOpBitFieldInsert:
|
||||
case SpvOpBitFieldSExtract:
|
||||
case SpvOpBitFieldUExtract:
|
||||
case SpvOpBitReverse:
|
||||
case SpvOpBitCount:
|
||||
case SpvOpCopyLogical:
|
||||
case SpvOpPtrEqual:
|
||||
case SpvOpPtrNotEqual:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
62
3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.h
vendored
Normal file
62
3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_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 TransformationMoveInstructionDown : public Transformation {
|
||||
public:
|
||||
explicit TransformationMoveInstructionDown(
|
||||
const protobufs::TransformationMoveInstructionDown& message);
|
||||
|
||||
explicit TransformationMoveInstructionDown(
|
||||
const protobufs::InstructionDescriptor& instruction);
|
||||
|
||||
// - |instruction| should be a descriptor of a valid instruction in the module
|
||||
// - |instruction|'s opcode should be supported by this transformation
|
||||
// - neither |instruction| nor its successor may be the last instruction in
|
||||
// the block
|
||||
// - |instruction|'s successor may not be dependent on the |instruction|
|
||||
// - it should be possible to insert |instruction|'s opcode after its
|
||||
// successor
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Swaps |instruction| with its successor.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
// Returns true if the |opcode| is supported by this transformation (i.e.
|
||||
// we can move an instruction with this opcode).
|
||||
static bool IsOpcodeSupported(SpvOp opcode);
|
||||
|
||||
protobufs::TransformationMoveInstructionDown message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
|
402
3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp
vendored
Normal file
402
3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp
vendored
Normal file
@ -0,0 +1,402 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_propagate_instruction_up.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst,
|
||||
uint32_t label_id) {
|
||||
assert(phi_inst.opcode() == SpvOpPhi && "|phi_inst| is not an OpPhi");
|
||||
|
||||
for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) {
|
||||
if (phi_inst.GetSingleWordInOperand(i) == label_id) {
|
||||
return phi_inst.GetSingleWordInOperand(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ContainsPointers(const opt::analysis::Type& type) {
|
||||
switch (type.kind()) {
|
||||
case opt::analysis::Type::kPointer:
|
||||
return true;
|
||||
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 ContainsPointers(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
const auto* inst_block = ir_context->get_instr_block(inst);
|
||||
assert(inst_block &&
|
||||
"This function shouldn't be applied to global instructions or function"
|
||||
"parameters");
|
||||
|
||||
for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
|
||||
const auto& operand = inst->GetInOperand(i);
|
||||
if (operand.type != SPV_OPERAND_TYPE_ID) {
|
||||
// Consider only <id> operands.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* dependency = ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
|
||||
assert(dependency && "Operand has invalid id");
|
||||
|
||||
if (ir_context->get_instr_block(dependency) == inst_block &&
|
||||
dependency->opcode() != SpvOpPhi) {
|
||||
// |dependency| is "valid" if it's an OpPhi from the same basic block or
|
||||
// an instruction from a different basic block.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
|
||||
const protobufs::TransformationPropagateInstructionUp& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
|
||||
uint32_t block_id,
|
||||
const std::map<uint32_t, uint32_t>& predecessor_id_to_fresh_id) {
|
||||
message_.set_block_id(block_id);
|
||||
*message_.mutable_predecessor_id_to_fresh_id() =
|
||||
fuzzerutil::MapToRepeatedUInt32Pair(predecessor_id_to_fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationPropagateInstructionUp::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that we can apply this transformation to the |block_id|.
|
||||
if (!IsApplicableToBlock(ir_context, message_.block_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.predecessor_id_to_fresh_id());
|
||||
for (auto id : ir_context->cfg()->preds(message_.block_id())) {
|
||||
// Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id|
|
||||
// map.
|
||||
if (!predecessor_id_to_fresh_id.count(id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> maybe_fresh_ids;
|
||||
maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size());
|
||||
for (const auto& entry : predecessor_id_to_fresh_id) {
|
||||
maybe_fresh_ids.push_back(entry.second);
|
||||
}
|
||||
|
||||
// All ids must be unique and fresh.
|
||||
return !fuzzerutil::HasDuplicates(maybe_fresh_ids) &&
|
||||
std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(),
|
||||
[ir_context](uint32_t id) {
|
||||
return fuzzerutil::IsFreshId(ir_context, id);
|
||||
});
|
||||
}
|
||||
|
||||
void TransformationPropagateInstructionUp::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto* inst = GetInstructionToPropagate(ir_context, message_.block_id());
|
||||
assert(inst &&
|
||||
"The block must have at least one supported instruction to propagate");
|
||||
assert(inst->result_id() && inst->type_id() &&
|
||||
"|inst| must have a result id and a type id");
|
||||
|
||||
opt::Instruction::OperandList op_phi_operands;
|
||||
const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.predecessor_id_to_fresh_id());
|
||||
std::unordered_set<uint32_t> visited_predecessors;
|
||||
for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) {
|
||||
// A block can have multiple identical predecessors.
|
||||
if (visited_predecessors.count(predecessor_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited_predecessors.insert(predecessor_id);
|
||||
|
||||
auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id);
|
||||
|
||||
// Compute InOperands for the OpPhi instruction to be inserted later.
|
||||
op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {new_result_id}});
|
||||
op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}});
|
||||
|
||||
// Create a clone of the |inst| to be inserted into the |predecessor_id|.
|
||||
std::unique_ptr<opt::Instruction> clone(inst->Clone(ir_context));
|
||||
clone->SetResultId(new_result_id);
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id);
|
||||
|
||||
// Adjust |clone|'s operands to account for possible dependencies on OpPhi
|
||||
// instructions from the same basic block.
|
||||
for (uint32_t i = 0; i < clone->NumInOperands(); ++i) {
|
||||
auto& operand = clone->GetInOperand(i);
|
||||
if (operand.type != SPV_OPERAND_TYPE_ID) {
|
||||
// Consider only ids.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* dependency_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
|
||||
assert(dependency_inst && "|clone| depends on an invalid id");
|
||||
|
||||
if (ir_context->get_instr_block(dependency_inst->result_id()) !=
|
||||
ir_context->cfg()->block(message_.block_id())) {
|
||||
// We don't need to adjust anything if |dependency_inst| is from a
|
||||
// different block, a global instruction or a function parameter.
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(dependency_inst->opcode() == SpvOpPhi &&
|
||||
"Propagated instruction can depend only on OpPhis from the same "
|
||||
"basic block or instructions from different basic blocks");
|
||||
|
||||
auto new_id = GetResultIdFromLabelId(*dependency_inst, predecessor_id);
|
||||
assert(new_id && "OpPhi instruction is missing a predecessor");
|
||||
operand.words[0] = new_id;
|
||||
}
|
||||
|
||||
auto* insert_before_inst = fuzzerutil::GetLastInsertBeforeInstruction(
|
||||
ir_context, predecessor_id, clone->opcode());
|
||||
assert(insert_before_inst && "Can't insert |clone| into |predecessor_id");
|
||||
|
||||
insert_before_inst->InsertBefore(std::move(clone));
|
||||
}
|
||||
|
||||
// Insert an OpPhi instruction into the basic block of |inst|.
|
||||
ir_context->get_instr_block(inst)->begin()->InsertBefore(
|
||||
MakeUnique<opt::Instruction>(ir_context, SpvOpPhi, inst->type_id(),
|
||||
inst->result_id(),
|
||||
std::move(op_phi_operands)));
|
||||
|
||||
// Remove |inst| from the basic block.
|
||||
ir_context->KillInst(inst);
|
||||
|
||||
// We have changed the module so most analyzes are now invalid.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationPropagateInstructionUp::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_propagate_instruction_up() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationPropagateInstructionUp::IsOpcodeSupported(SpvOp opcode) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
|
||||
// We only support "simple" instructions that don't work with memory.
|
||||
// We should extend this so that we support the ones that modify the memory
|
||||
// too.
|
||||
switch (opcode) {
|
||||
case SpvOpUndef:
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
case SpvOpArrayLength:
|
||||
case SpvOpVectorExtractDynamic:
|
||||
case SpvOpVectorInsertDynamic:
|
||||
case SpvOpVectorShuffle:
|
||||
case SpvOpCompositeConstruct:
|
||||
case SpvOpCompositeExtract:
|
||||
case SpvOpCompositeInsert:
|
||||
case SpvOpCopyObject:
|
||||
case SpvOpTranspose:
|
||||
case SpvOpConvertFToU:
|
||||
case SpvOpConvertFToS:
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
case SpvOpUConvert:
|
||||
case SpvOpSConvert:
|
||||
case SpvOpFConvert:
|
||||
case SpvOpQuantizeToF16:
|
||||
case SpvOpSatConvertSToU:
|
||||
case SpvOpSatConvertUToS:
|
||||
case SpvOpBitcast:
|
||||
case SpvOpSNegate:
|
||||
case SpvOpFNegate:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpFAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpFSub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpFMul:
|
||||
case SpvOpUDiv:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpFDiv:
|
||||
case SpvOpUMod:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpFRem:
|
||||
case SpvOpFMod:
|
||||
case SpvOpVectorTimesScalar:
|
||||
case SpvOpMatrixTimesScalar:
|
||||
case SpvOpVectorTimesMatrix:
|
||||
case SpvOpMatrixTimesVector:
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
case SpvOpOuterProduct:
|
||||
case SpvOpDot:
|
||||
case SpvOpIAddCarry:
|
||||
case SpvOpISubBorrow:
|
||||
case SpvOpUMulExtended:
|
||||
case SpvOpSMulExtended:
|
||||
case SpvOpAny:
|
||||
case SpvOpAll:
|
||||
case SpvOpIsNan:
|
||||
case SpvOpIsInf:
|
||||
case SpvOpIsFinite:
|
||||
case SpvOpIsNormal:
|
||||
case SpvOpSignBitSet:
|
||||
case SpvOpLessOrGreater:
|
||||
case SpvOpOrdered:
|
||||
case SpvOpUnordered:
|
||||
case SpvOpLogicalEqual:
|
||||
case SpvOpLogicalNotEqual:
|
||||
case SpvOpLogicalOr:
|
||||
case SpvOpLogicalAnd:
|
||||
case SpvOpLogicalNot:
|
||||
case SpvOpSelect:
|
||||
case SpvOpIEqual:
|
||||
case SpvOpINotEqual:
|
||||
case SpvOpUGreaterThan:
|
||||
case SpvOpSGreaterThan:
|
||||
case SpvOpUGreaterThanEqual:
|
||||
case SpvOpSGreaterThanEqual:
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan:
|
||||
case SpvOpULessThanEqual:
|
||||
case SpvOpSLessThanEqual:
|
||||
case SpvOpFOrdEqual:
|
||||
case SpvOpFUnordEqual:
|
||||
case SpvOpFOrdNotEqual:
|
||||
case SpvOpFUnordNotEqual:
|
||||
case SpvOpFOrdLessThan:
|
||||
case SpvOpFUnordLessThan:
|
||||
case SpvOpFOrdGreaterThan:
|
||||
case SpvOpFUnordGreaterThan:
|
||||
case SpvOpFOrdLessThanEqual:
|
||||
case SpvOpFUnordLessThanEqual:
|
||||
case SpvOpFOrdGreaterThanEqual:
|
||||
case SpvOpFUnordGreaterThanEqual:
|
||||
case SpvOpShiftRightLogical:
|
||||
case SpvOpShiftRightArithmetic:
|
||||
case SpvOpShiftLeftLogical:
|
||||
case SpvOpBitwiseOr:
|
||||
case SpvOpBitwiseXor:
|
||||
case SpvOpBitwiseAnd:
|
||||
case SpvOpNot:
|
||||
case SpvOpBitFieldInsert:
|
||||
case SpvOpBitFieldSExtract:
|
||||
case SpvOpBitFieldUExtract:
|
||||
case SpvOpBitReverse:
|
||||
case SpvOpBitCount:
|
||||
case SpvOpCopyLogical:
|
||||
case SpvOpPtrEqual:
|
||||
case SpvOpPtrNotEqual:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
opt::Instruction*
|
||||
TransformationPropagateInstructionUp::GetInstructionToPropagate(
|
||||
opt::IRContext* ir_context, uint32_t block_id) {
|
||||
auto* block = ir_context->cfg()->block(block_id);
|
||||
assert(block && "|block_id| is invalid");
|
||||
|
||||
for (auto& inst : *block) {
|
||||
// We look for the first instruction in the block that satisfies the
|
||||
// following rules:
|
||||
// - it's not an OpPhi
|
||||
// - it must be supported by this transformation
|
||||
// - it may depend only on instructions from different basic blocks or on
|
||||
// OpPhi instructions from the same basic block.
|
||||
if (inst.opcode() == SpvOpPhi || !IsOpcodeSupported(inst.opcode()) ||
|
||||
!inst.type_id() || !inst.result_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* inst_type = ir_context->get_type_mgr()->GetType(inst.type_id());
|
||||
assert(inst_type && "|inst| has invalid type");
|
||||
|
||||
if (!ir_context->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityVariablePointersStorageBuffer) &&
|
||||
ContainsPointers(*inst_type)) {
|
||||
// OpPhi supports pointer operands only with VariablePointers or
|
||||
// VariablePointersStorageBuffer capabilities.
|
||||
//
|
||||
// Note that VariablePointers capability implicitly declares
|
||||
// VariablePointersStorageBuffer capability.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!HasValidDependencies(ir_context, &inst)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return &inst;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TransformationPropagateInstructionUp::IsApplicableToBlock(
|
||||
opt::IRContext* ir_context, uint32_t block_id) {
|
||||
// Check that |block_id| is valid.
|
||||
const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
|
||||
if (!label_inst || label_inst->opcode() != SpvOpLabel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |block| has predecessors.
|
||||
const auto& predecessors = ir_context->cfg()->preds(block_id);
|
||||
if (predecessors.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The block must contain an instruction to propagate.
|
||||
const auto* inst_to_propagate =
|
||||
GetInstructionToPropagate(ir_context, block_id);
|
||||
if (!inst_to_propagate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should be able to insert |inst_to_propagate| into every predecessor of
|
||||
// |block|.
|
||||
return std::all_of(predecessors.begin(), predecessors.end(),
|
||||
[ir_context, inst_to_propagate](uint32_t predecessor_id) {
|
||||
return fuzzerutil::GetLastInsertBeforeInstruction(
|
||||
ir_context, predecessor_id,
|
||||
inst_to_propagate->opcode()) != nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
89
3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h
vendored
Normal file
89
3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#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 TransformationPropagateInstructionUp : public Transformation {
|
||||
public:
|
||||
explicit TransformationPropagateInstructionUp(
|
||||
const protobufs::TransformationPropagateInstructionUp& message);
|
||||
|
||||
TransformationPropagateInstructionUp(
|
||||
uint32_t block_id,
|
||||
const std::map<uint32_t, uint32_t>& predecessor_id_to_fresh_id);
|
||||
|
||||
// - |block_id| must be a valid result id of some OpLabel instruction.
|
||||
// - |block_id| must have at least one predecessor
|
||||
// - |block_id| must contain an instruction that can be propagated using this
|
||||
// transformation
|
||||
// - the instruction can be propagated if:
|
||||
// - it's not an OpPhi
|
||||
// - it is supported by this transformation
|
||||
// - it depends only on instructions from different basic blocks or on
|
||||
// OpPhi instructions from the same basic block
|
||||
// - it should be possible to insert the propagated instruction at the end of
|
||||
// each |block_id|'s predecessor
|
||||
// - |predecessor_id_to_fresh_id| must have an entry for at least every
|
||||
// predecessor of |block_id|
|
||||
// - each value in the |predecessor_id_to_fresh_id| map must be a fresh id
|
||||
// - all fresh ids in the |predecessor_id_to_fresh_id| must be unique
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Inserts a copy of the propagated instruction into each |block_id|'s
|
||||
// predecessor. Replaces the original instruction with an OpPhi referring
|
||||
// inserted copies.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if this transformation can be applied to the block with id
|
||||
// |block_id|. Concretely, returns true iff:
|
||||
// - |block_id| is a valid id of some block in the module
|
||||
// - |block_id| has predecessors
|
||||
// - |block_id| contains an instruction that can be propagated
|
||||
// - it is possible to insert the propagated instruction into every
|
||||
// |block_id|'s predecessor
|
||||
static bool IsApplicableToBlock(opt::IRContext* ir_context,
|
||||
uint32_t block_id);
|
||||
|
||||
private:
|
||||
// Returns the instruction that will be propagated into the predecessors of
|
||||
// the |block_id|. Returns nullptr if no such an instruction exists.
|
||||
static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context,
|
||||
uint32_t block_id);
|
||||
|
||||
// Returns true if |opcode| is supported by this transformation.
|
||||
static bool IsOpcodeSupported(SpvOp opcode);
|
||||
|
||||
protobufs::TransformationPropagateInstructionUp message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_
|
232
3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
vendored
Normal file
232
3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
// 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_replace_add_sub_mul_with_carrying_extended.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kOpCompositeExtractIndexLowOrderBits = 0;
|
||||
const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
|
||||
const uint32_t kArithmeticInstructionIndexRightInOperand = 1;
|
||||
} // namespace
|
||||
|
||||
TransformationReplaceAddSubMulWithCarryingExtended::
|
||||
TransformationReplaceAddSubMulWithCarryingExtended(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationReplaceAddSubMulWithCarryingExtended& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationReplaceAddSubMulWithCarryingExtended::
|
||||
TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id,
|
||||
uint32_t result_id) {
|
||||
message_.set_struct_fresh_id(struct_fresh_id);
|
||||
message_.set_result_id(result_id);
|
||||
}
|
||||
|
||||
bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |message_.struct_fresh_id| must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul
|
||||
// instruction. The instruction must be defined.
|
||||
auto instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (instruction == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!TransformationReplaceAddSubMulWithCarryingExtended::
|
||||
IsInstructionSuitable(ir_context, *instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The struct type for holding the intermediate result must exist in the
|
||||
// module. The struct type is based on the operand type.
|
||||
uint32_t operand_type_id = ir_context->get_def_use_mgr()
|
||||
->GetDef(instruction->GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexLeftInOperand))
|
||||
->type_id();
|
||||
|
||||
uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
|
||||
ir_context, {operand_type_id, operand_type_id});
|
||||
if (struct_type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationReplaceAddSubMulWithCarryingExtended::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// |message_.struct_fresh_id| must be fresh.
|
||||
assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) &&
|
||||
"|message_.struct_fresh_id| must be fresh");
|
||||
|
||||
// Get the signedness of an operand if it is an int or the signedness of a
|
||||
// component if it is a vector.
|
||||
auto type_id =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id();
|
||||
auto type = ir_context->get_type_mgr()->GetType(type_id);
|
||||
bool operand_is_signed;
|
||||
if (type->kind() == opt::analysis::Type::kVector) {
|
||||
auto operand_type = type->AsVector()->element_type();
|
||||
operand_is_signed = operand_type->AsInteger()->IsSigned();
|
||||
} else {
|
||||
operand_is_signed = type->AsInteger()->IsSigned();
|
||||
}
|
||||
|
||||
auto original_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id());
|
||||
|
||||
// Determine the opcode of the new instruction that computes the result into a
|
||||
// struct.
|
||||
SpvOp new_instruction_opcode;
|
||||
|
||||
switch (original_instruction->opcode()) {
|
||||
case SpvOpIAdd:
|
||||
new_instruction_opcode = SpvOpIAddCarry;
|
||||
break;
|
||||
case SpvOpISub:
|
||||
new_instruction_opcode = SpvOpISubBorrow;
|
||||
break;
|
||||
case SpvOpIMul:
|
||||
if (!operand_is_signed) {
|
||||
new_instruction_opcode = SpvOpUMulExtended;
|
||||
} else {
|
||||
new_instruction_opcode = SpvOpSMulExtended;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false && "The instruction has an unsupported opcode.");
|
||||
return;
|
||||
}
|
||||
// Get the type of struct type id holding the intermediate result based on the
|
||||
// operand type.
|
||||
uint32_t operand_type_id =
|
||||
ir_context->get_def_use_mgr()
|
||||
->GetDef(original_instruction->GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexLeftInOperand))
|
||||
->type_id();
|
||||
|
||||
uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
|
||||
ir_context, {operand_type_id, operand_type_id});
|
||||
// Avoid unused variables in release mode.
|
||||
(void)struct_type_id;
|
||||
assert(struct_type_id && "The struct type must exist in the module.");
|
||||
|
||||
// Insert the new instruction that computes the result into a struct before
|
||||
// the |original_instruction|.
|
||||
original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, new_instruction_opcode, struct_type_id,
|
||||
message_.struct_fresh_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID,
|
||||
{original_instruction->GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexLeftInOperand)}},
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{original_instruction->GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexRightInOperand)}}})));
|
||||
|
||||
// Insert the OpCompositeExtract after the added instruction. This instruction
|
||||
// takes the first component of the struct which represents low-order bits of
|
||||
// the operation. This is the original result.
|
||||
original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract, original_instruction->type_id(),
|
||||
message_.result_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||
{kOpCompositeExtractIndexLowOrderBits}}})));
|
||||
|
||||
// Remove the original instruction.
|
||||
ir_context->KillInst(original_instruction);
|
||||
|
||||
// We have modified the module so most analyzes are now invalid.
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable(
|
||||
opt::IRContext* ir_context, const opt::Instruction& instruction) {
|
||||
auto instruction_opcode = instruction.opcode();
|
||||
|
||||
// Only instructions OpIAdd, OpISub, OpIMul are supported.
|
||||
switch (instruction_opcode) {
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpIMul:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint32_t operand_1_type_id =
|
||||
ir_context->get_def_use_mgr()
|
||||
->GetDef(instruction.GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexLeftInOperand))
|
||||
->type_id();
|
||||
|
||||
uint32_t operand_2_type_id =
|
||||
ir_context->get_def_use_mgr()
|
||||
->GetDef(instruction.GetSingleWordInOperand(
|
||||
kArithmeticInstructionIndexRightInOperand))
|
||||
->type_id();
|
||||
|
||||
uint32_t result_type_id = instruction.type_id();
|
||||
|
||||
// Both type ids of the operands and the result type ids must be equal.
|
||||
if (operand_1_type_id != operand_2_type_id) {
|
||||
return false;
|
||||
}
|
||||
if (operand_2_type_id != result_type_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In case of OpIAdd and OpISub, the type must be unsigned.
|
||||
auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
|
||||
|
||||
switch (instruction_opcode) {
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub: {
|
||||
// In case of OpIAdd and OpISub if the operand is a vector, the component
|
||||
// type must be unsigned. Otherwise (if the operand is an int), the
|
||||
// operand must be unsigned.
|
||||
bool operand_is_signed =
|
||||
type->AsVector()
|
||||
? type->AsVector()->element_type()->AsInteger()->IsSigned()
|
||||
: type->AsInteger()->IsSigned();
|
||||
if (operand_is_signed) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protobufs::Transformation
|
||||
TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_replace_add_sub_mul_with_carrying_extended() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
69
3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
vendored
Normal file
69
3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
// 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_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
|
||||
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_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 TransformationReplaceAddSubMulWithCarryingExtended
|
||||
: public Transformation {
|
||||
public:
|
||||
explicit TransformationReplaceAddSubMulWithCarryingExtended(
|
||||
const protobufs::TransformationReplaceAddSubMulWithCarryingExtended&
|
||||
message);
|
||||
|
||||
explicit TransformationReplaceAddSubMulWithCarryingExtended(
|
||||
uint32_t struct_fresh_id, uint32_t result_id);
|
||||
|
||||
// - |message_.struct_fresh_id| must be fresh.
|
||||
// - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul
|
||||
// instruction. In this instruction the result type id and the type ids of
|
||||
// the operands must be the same.
|
||||
// - The type of struct holding the intermediate result must exists in the
|
||||
// module.
|
||||
// - For OpIAdd, OpISub both operands must be unsigned.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// A transformation that replaces instructions OpIAdd, OpISub, OpIMul with
|
||||
// pairs of instructions. The first one (OpIAddCarry, OpISubBorrow,
|
||||
// OpUMulExtended, OpSMulExtended) computes the result into a struct. The
|
||||
// second one extracts the appropriate component from the struct to yield the
|
||||
// original result.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the
|
||||
// transformation.
|
||||
bool static IsInstructionSuitable(opt::IRContext* ir_context,
|
||||
const opt::Instruction& instruction);
|
||||
|
||||
private:
|
||||
protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
|
@ -90,6 +90,40 @@ TransformationReplaceConstantWithUniform::MakeLoadInstruction(
|
||||
operands_for_load);
|
||||
}
|
||||
|
||||
opt::Instruction*
|
||||
TransformationReplaceConstantWithUniform::GetInsertBeforeInstruction(
|
||||
opt::IRContext* ir_context) const {
|
||||
auto* result =
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The use might be in an OpPhi instruction.
|
||||
if (result->opcode() == SpvOpPhi) {
|
||||
// OpPhi instructions must be the first instructions in a block. Thus, we
|
||||
// can't insert above the OpPhi instruction. Given the predecessor block
|
||||
// that corresponds to the id use, get the last instruction in that block
|
||||
// above which we can insert OpAccessChain and OpLoad.
|
||||
return fuzzerutil::GetLastInsertBeforeInstruction(
|
||||
ir_context,
|
||||
result->GetSingleWordInOperand(
|
||||
message_.id_use_descriptor().in_operand_index() + 1),
|
||||
SpvOpLoad);
|
||||
}
|
||||
|
||||
// The only operand that we could've replaced in the OpBranchConditional is
|
||||
// the condition id. But that operand has a boolean type and uniform variables
|
||||
// can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be
|
||||
// an OpBranchConditional.
|
||||
assert(result->opcode() != SpvOpBranchConditional &&
|
||||
"OpBranchConditional has no operands to replace");
|
||||
|
||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, result) &&
|
||||
"We should be able to insert OpLoad and OpAccessChain at this point");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationReplaceConstantWithUniform::IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
@ -188,6 +222,12 @@ bool TransformationReplaceConstantWithUniform::IsApplicable(
|
||||
}
|
||||
}
|
||||
|
||||
// Once all checks are completed, we should be able to safely insert
|
||||
// OpAccessChain and OpLoad into the module.
|
||||
assert(GetInsertBeforeInstruction(ir_context) &&
|
||||
"There must exist an instruction that we can use to insert "
|
||||
"OpAccessChain and OpLoad above");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -195,7 +235,7 @@ void TransformationReplaceConstantWithUniform::Apply(
|
||||
spvtools::opt::IRContext* ir_context,
|
||||
TransformationContext* /*unused*/) const {
|
||||
// Get the instruction that contains the id use we wish to replace.
|
||||
auto instruction_containing_constant_use =
|
||||
auto* instruction_containing_constant_use =
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
||||
assert(instruction_containing_constant_use &&
|
||||
"Precondition requires that the id use can be found.");
|
||||
@ -210,12 +250,17 @@ void TransformationReplaceConstantWithUniform::Apply(
|
||||
->GetDef(message_.id_use_descriptor().id_of_interest())
|
||||
->type_id();
|
||||
|
||||
// Get an instruction that will be used to insert OpAccessChain and OpLoad.
|
||||
auto* insert_before_inst = GetInsertBeforeInstruction(ir_context);
|
||||
assert(insert_before_inst &&
|
||||
"There must exist an insertion point for OpAccessChain and OpLoad");
|
||||
|
||||
// Add an access chain instruction to target the uniform element.
|
||||
instruction_containing_constant_use->InsertBefore(
|
||||
insert_before_inst->InsertBefore(
|
||||
MakeAccessChainInstruction(ir_context, constant_type_id));
|
||||
|
||||
// Add a load from this access chain.
|
||||
instruction_containing_constant_use->InsertBefore(
|
||||
insert_before_inst->InsertBefore(
|
||||
MakeLoadInstruction(ir_context, constant_type_id));
|
||||
|
||||
// Adjust the instruction containing the usage of the constant so that this
|
||||
|
@ -84,6 +84,11 @@ class TransformationReplaceConstantWithUniform : public Transformation {
|
||||
std::unique_ptr<opt::Instruction> MakeLoadInstruction(
|
||||
spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const;
|
||||
|
||||
// OpAccessChain and OpLoad will be inserted above the instruction returned
|
||||
// by this function. Returns nullptr if no such instruction is present.
|
||||
opt::Instruction* GetInsertBeforeInstruction(
|
||||
opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationReplaceConstantWithUniform message_;
|
||||
};
|
||||
|
||||
|
@ -59,14 +59,6 @@ bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// It must be valid to insert the OpStore and OpLoad instruction before it.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
|
||||
copy_object_instruction) ||
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
|
||||
copy_object_instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pointer type instruction pointing to the value type must be defined.
|
||||
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
|
||||
ir_context, copy_object_instruction->type_id(),
|
||||
|
@ -109,7 +109,7 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
|
||||
bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
|
||||
opt::IRContext* ir_context, opt::Instruction* use_instruction,
|
||||
uint32_t use_in_operand_index) {
|
||||
if (use_instruction->opcode() == SpvOpAccessChain &&
|
||||
if (spvOpcodeIsAccessChain(use_instruction->opcode()) &&
|
||||
use_in_operand_index > 0) {
|
||||
// This is an access chain index. If the (sub-)object being accessed by the
|
||||
// given index has struct type then we cannot replace the use with a
|
||||
|
@ -45,26 +45,18 @@ bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// This transformation is only applicable to the pair of OpLoad and OpStore
|
||||
// instructions.
|
||||
if (message_.load_instruction_descriptor().target_instruction_opcode() !=
|
||||
SpvOpLoad) {
|
||||
return false;
|
||||
}
|
||||
if (message_.store_instruction_descriptor().target_instruction_opcode() !=
|
||||
SpvOpStore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpLoad instruction must be defined.
|
||||
auto load_instruction =
|
||||
FindInstruction(message_.load_instruction_descriptor(), ir_context);
|
||||
if (!load_instruction) {
|
||||
if (!load_instruction || load_instruction->opcode() != SpvOpLoad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpStore instruction must be defined.
|
||||
auto store_instruction =
|
||||
FindInstruction(message_.store_instruction_descriptor(), ir_context);
|
||||
if (!store_instruction) {
|
||||
if (!store_instruction || store_instruction->opcode() != SpvOpStore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,8 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
const auto caller_id_to_fresh_composite_id =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.caller_id_to_fresh_composite_id());
|
||||
|
||||
// Check that |callee_id_to_fresh_composite_id| is valid.
|
||||
@ -151,23 +152,11 @@ void TransformationReplaceParamsWithStruct::Apply(
|
||||
// Compute indices of replaced parameters. This will be used to adjust
|
||||
// OpFunctionCall instructions and create OpCompositeConstruct instructions at
|
||||
// every call site.
|
||||
std::vector<uint32_t> indices_of_replaced_params;
|
||||
{
|
||||
// We want to destroy |params| after the loop because it will contain
|
||||
// dangling pointers when we remove parameters from the function.
|
||||
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
|
||||
for (auto id : message_.parameter_id()) {
|
||||
auto it = std::find_if(params.begin(), params.end(),
|
||||
[id](const opt::Instruction* param) {
|
||||
return param->result_id() == id;
|
||||
});
|
||||
assert(it != params.end() && "Parameter's id is invalid");
|
||||
indices_of_replaced_params.push_back(
|
||||
static_cast<uint32_t>(it - params.begin()));
|
||||
}
|
||||
}
|
||||
const auto indices_of_replaced_params =
|
||||
ComputeIndicesOfReplacedParameters(ir_context);
|
||||
|
||||
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
const auto caller_id_to_fresh_composite_id =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.caller_id_to_fresh_composite_id());
|
||||
|
||||
// Update all function calls.
|
||||
@ -182,12 +171,13 @@ void TransformationReplaceParamsWithStruct::Apply(
|
||||
}
|
||||
|
||||
// Remove arguments from the function call. We do it in a separate loop
|
||||
// and in reverse order to make sure we have removed correct operands.
|
||||
for (auto it = indices_of_replaced_params.rbegin();
|
||||
it != indices_of_replaced_params.rend(); ++it) {
|
||||
// and in decreasing order to make sure we have removed correct operands.
|
||||
for (auto index : std::set<uint32_t, std::greater<uint32_t>>(
|
||||
indices_of_replaced_params.begin(),
|
||||
indices_of_replaced_params.end())) {
|
||||
// +1 since the first in operand to OpFunctionCall is the result id of
|
||||
// the function.
|
||||
inst->RemoveInOperand(*it + 1);
|
||||
inst->RemoveInOperand(index + 1);
|
||||
}
|
||||
|
||||
// Insert OpCompositeConstruct before the function call.
|
||||
@ -305,5 +295,30 @@ uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
|
||||
return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
TransformationReplaceParamsWithStruct::ComputeIndicesOfReplacedParameters(
|
||||
opt::IRContext* ir_context) const {
|
||||
assert(!message_.parameter_id().empty() &&
|
||||
"There must be at least one parameter to replace");
|
||||
|
||||
const auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||
ir_context, message_.parameter_id(0));
|
||||
assert(function && "|parameter_id|s are invalid");
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
|
||||
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
|
||||
for (auto id : message_.parameter_id()) {
|
||||
auto it = std::find_if(params.begin(), params.end(),
|
||||
[id](const opt::Instruction* param) {
|
||||
return param->result_id() == id;
|
||||
});
|
||||
assert(it != params.end() && "Parameter's id is invalid");
|
||||
result.push_back(static_cast<uint32_t>(it - params.begin()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -73,6 +73,12 @@ class TransformationReplaceParamsWithStruct : public Transformation {
|
||||
// transformation (see docs on the IsApplicable method to learn more).
|
||||
uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
|
||||
|
||||
// Returns a vector of indices of parameters to replace. Concretely, i'th
|
||||
// element is the index of the parameter with result id |parameter_id[i]| in
|
||||
// its function.
|
||||
std::vector<uint32_t> ComputeIndicesOfReplacedParameters(
|
||||
opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationReplaceParamsWithStruct message_;
|
||||
};
|
||||
|
||||
|
12
3rdparty/spirv-tools/source/opcode.cpp
vendored
12
3rdparty/spirv-tools/source/opcode.cpp
vendored
@ -719,3 +719,15 @@ std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool spvOpcodeIsAccessChain(SpvOp opcode) {
|
||||
switch (opcode) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
case SpvOpPtrAccessChain:
|
||||
case SpvOpInBoundsPtrAccessChain:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
3
3rdparty/spirv-tools/source/opcode.h
vendored
3
3rdparty/spirv-tools/source/opcode.h
vendored
@ -144,4 +144,7 @@ bool spvOpcodeIsImageSample(SpvOp opcode);
|
||||
// operands for |opcode|.
|
||||
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
|
||||
|
||||
// Returns true for opcodes that represents access chain instructions.
|
||||
bool spvOpcodeIsAccessChain(SpvOp opcode);
|
||||
|
||||
#endif // SOURCE_OPCODE_H_
|
||||
|
@ -226,7 +226,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
wrap_opkill.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "source/cfa.h"
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/opt/eliminate_dead_functions_util.h"
|
||||
#include "source/opt/iterator.h"
|
||||
#include "source/opt/reflect.h"
|
||||
#include "source/spirv_constant.h"
|
||||
@ -727,8 +728,8 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
|
||||
funcIter != get_module()->end();) {
|
||||
if (live_function_set.count(&*funcIter) == 0) {
|
||||
modified = true;
|
||||
EliminateFunction(&*funcIter);
|
||||
funcIter = funcIter.Erase();
|
||||
funcIter =
|
||||
eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter);
|
||||
} else {
|
||||
++funcIter;
|
||||
}
|
||||
@ -737,12 +738,6 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::EliminateFunction(Function* func) {
|
||||
// Remove all of the instruction in the function body
|
||||
func->ForEachInst([this](Instruction* inst) { context()->KillInst(inst); },
|
||||
true);
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
// Remove debug and annotation statements referencing dead instructions.
|
||||
// This must be done before killing the instructions, otherwise there are
|
||||
|
@ -127,9 +127,6 @@ class AggressiveDCEPass : public MemPass {
|
||||
// Erases functions that are unreachable from the entry points of the module.
|
||||
bool EliminateDeadFunctions();
|
||||
|
||||
// Removes |func| from the module and deletes all its instructions.
|
||||
void EliminateFunction(Function* func);
|
||||
|
||||
// For function |func|, mark all Stores to non-function-scope variables
|
||||
// and block terminating instructions as live. Recursively mark the values
|
||||
// they use. When complete, mark any non-live instructions to be deleted.
|
||||
|
@ -196,6 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) {
|
||||
}
|
||||
const uint32_t id = ii->result_id();
|
||||
get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) {
|
||||
if (user->IsOpenCL100DebugInstr()) return;
|
||||
switch (user->opcode()) {
|
||||
case SpvOpCompositeInsert:
|
||||
case SpvOpPhi:
|
||||
|
@ -21,9 +21,35 @@ namespace eliminatedeadfunctionsutil {
|
||||
|
||||
Module::iterator EliminateFunction(IRContext* context,
|
||||
Module::iterator* func_iter) {
|
||||
bool first_func = *func_iter == context->module()->begin();
|
||||
bool seen_func_end = false;
|
||||
(*func_iter)
|
||||
->ForEachInst([context](Instruction* inst) { context->KillInst(inst); },
|
||||
true);
|
||||
->ForEachInst(
|
||||
[context, first_func, func_iter, &seen_func_end](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpFunctionEnd) {
|
||||
seen_func_end = true;
|
||||
}
|
||||
// Move non-semantic instructions to the previous function or
|
||||
// global values if this is the first function.
|
||||
if (seen_func_end && inst->opcode() == SpvOpExtInst) {
|
||||
assert(inst->IsNonSemanticInstruction());
|
||||
std::unique_ptr<Instruction> clone(inst->Clone(context));
|
||||
context->ForgetUses(inst);
|
||||
context->AnalyzeDefUse(clone.get());
|
||||
if (first_func) {
|
||||
context->AddGlobalValue(std::move(clone));
|
||||
} else {
|
||||
auto prev_func_iter = *func_iter;
|
||||
--prev_func_iter;
|
||||
prev_func_iter->AddNonSemanticInstruction(std::move(clone));
|
||||
}
|
||||
inst->ToNop();
|
||||
} else {
|
||||
context->KillNonSemanticInfo(inst);
|
||||
context->KillInst(inst);
|
||||
}
|
||||
},
|
||||
true, true);
|
||||
return func_iter->Erase();
|
||||
}
|
||||
|
||||
|
@ -1467,7 +1467,7 @@ FoldingRule CompositeConstructFeedingExtract() {
|
||||
type_mgr->GetType(element_def->type_id())->AsVector();
|
||||
if (element_type) {
|
||||
uint32_t vector_size = element_type->element_count();
|
||||
if (vector_size < element_index) {
|
||||
if (vector_size <= element_index) {
|
||||
// The element we want comes after this vector.
|
||||
element_index -= vector_size;
|
||||
} else {
|
||||
|
54
3rdparty/spirv-tools/source/opt/function.cpp
vendored
54
3rdparty/spirv-tools/source/opt/function.cpp
vendored
@ -47,31 +47,40 @@ Function* Function::Clone(IRContext* ctx) const {
|
||||
}
|
||||
|
||||
clone->SetFunctionEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx)));
|
||||
|
||||
clone->non_semantic_.reserve(non_semantic_.size());
|
||||
for (auto& non_semantic : non_semantic_) {
|
||||
clone->AddNonSemanticInstruction(
|
||||
std::unique_ptr<Instruction>(non_semantic->Clone(ctx)));
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
void Function::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts) {
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) {
|
||||
WhileEachInst(
|
||||
[&f](Instruction* inst) {
|
||||
f(inst);
|
||||
return true;
|
||||
},
|
||||
run_on_debug_line_insts);
|
||||
run_on_debug_line_insts, run_on_non_semantic_insts);
|
||||
}
|
||||
|
||||
void Function::ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts) const {
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) const {
|
||||
WhileEachInst(
|
||||
[&f](const Instruction* inst) {
|
||||
f(inst);
|
||||
return true;
|
||||
},
|
||||
run_on_debug_line_insts);
|
||||
run_on_debug_line_insts, run_on_non_semantic_insts);
|
||||
}
|
||||
|
||||
bool Function::WhileEachInst(const std::function<bool(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts) {
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) {
|
||||
if (def_inst_) {
|
||||
if (!def_inst_->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
return false;
|
||||
@ -99,13 +108,26 @@ bool Function::WhileEachInst(const std::function<bool(Instruction*)>& f,
|
||||
}
|
||||
}
|
||||
|
||||
if (end_inst_) return end_inst_->WhileEachInst(f, run_on_debug_line_insts);
|
||||
if (end_inst_) {
|
||||
if (!end_inst_->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_on_non_semantic_insts) {
|
||||
for (auto& non_semantic : non_semantic_) {
|
||||
if (!non_semantic->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Function::WhileEachInst(const std::function<bool(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts) const {
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) const {
|
||||
if (def_inst_) {
|
||||
if (!static_cast<const Instruction*>(def_inst_.get())
|
||||
->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
@ -133,9 +155,21 @@ bool Function::WhileEachInst(const std::function<bool(const Instruction*)>& f,
|
||||
}
|
||||
}
|
||||
|
||||
if (end_inst_)
|
||||
return static_cast<const Instruction*>(end_inst_.get())
|
||||
->WhileEachInst(f, run_on_debug_line_insts);
|
||||
if (end_inst_) {
|
||||
if (!static_cast<const Instruction*>(end_inst_.get())
|
||||
->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_on_non_semantic_insts) {
|
||||
for (auto& non_semantic : non_semantic_) {
|
||||
if (!static_cast<const Instruction*>(non_semantic.get())
|
||||
->WhileEachInst(f, run_on_debug_line_insts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
33
3rdparty/spirv-tools/source/opt/function.h
vendored
33
3rdparty/spirv-tools/source/opt/function.h
vendored
@ -79,6 +79,11 @@ class Function {
|
||||
// Saves the given function end instruction.
|
||||
inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
|
||||
|
||||
// Add a non-semantic instruction that succeeds this function in the module.
|
||||
// These instructions are maintained in the order they are added.
|
||||
inline void AddNonSemanticInstruction(
|
||||
std::unique_ptr<Instruction> non_semantic);
|
||||
|
||||
// Returns the given function end instruction.
|
||||
inline Instruction* EndInst() { return end_inst_.get(); }
|
||||
inline const Instruction* EndInst() const { return end_inst_.get(); }
|
||||
@ -115,19 +120,24 @@ class Function {
|
||||
}
|
||||
|
||||
// Runs the given function |f| on instructions in this function, in order,
|
||||
// and optionally on debug line instructions that might precede them.
|
||||
// and optionally on debug line instructions that might precede them and
|
||||
// non-semantic instructions that succceed the function.
|
||||
void ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false);
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false);
|
||||
void ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false) const;
|
||||
// Runs the given function |f| on instructions in this function, in order,
|
||||
// and optionally on debug line instructions that might precede them.
|
||||
// If |f| returns false, iteration is terminated and this function returns
|
||||
// false.
|
||||
// and optionally on debug line instructions that might precede them and
|
||||
// non-semantic instructions that succeed the function. If |f| returns
|
||||
// false, iteration is terminated and this function returns false.
|
||||
bool WhileEachInst(const std::function<bool(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false);
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false);
|
||||
bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false) const;
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false) const;
|
||||
|
||||
// Runs the given function |f| on each parameter instruction in this function,
|
||||
// in order, and optionally on debug line instructions that might precede
|
||||
@ -172,6 +182,8 @@ class Function {
|
||||
std::vector<std::unique_ptr<BasicBlock>> blocks_;
|
||||
// The OpFunctionEnd instruction.
|
||||
std::unique_ptr<Instruction> end_inst_;
|
||||
// Non-semantic instructions succeeded by this function.
|
||||
std::vector<std::unique_ptr<Instruction>> non_semantic_;
|
||||
};
|
||||
|
||||
// Pretty-prints |func| to |str|. Returns |str|.
|
||||
@ -235,6 +247,11 @@ inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
|
||||
end_inst_ = std::move(end_inst);
|
||||
}
|
||||
|
||||
inline void Function::AddNonSemanticInstruction(
|
||||
std::unique_ptr<Instruction> non_semantic) {
|
||||
non_semantic_.emplace_back(std::move(non_semantic));
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
39
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
39
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
@ -183,8 +183,9 @@ void Instruction::ToBinaryWithoutAttachedDebugInsts(
|
||||
std::vector<uint32_t>* binary) const {
|
||||
const uint32_t num_words = 1 + NumOperandWords();
|
||||
binary->push_back((num_words << 16) | static_cast<uint16_t>(opcode_));
|
||||
for (const auto& operand : operands_)
|
||||
for (const auto& operand : operands_) {
|
||||
binary->insert(binary->end(), operand.words.begin(), operand.words.end());
|
||||
}
|
||||
}
|
||||
|
||||
void Instruction::ReplaceOperands(const OperandList& new_operands) {
|
||||
@ -283,8 +284,7 @@ bool Instruction::IsVulkanStorageImage() const {
|
||||
|
||||
// Check if the image is sampled. If we do not know for sure that it is,
|
||||
// then assume it is a storage image.
|
||||
auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
|
||||
return s != 1;
|
||||
return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
|
||||
}
|
||||
|
||||
bool Instruction::IsVulkanSampledImage() const {
|
||||
@ -318,8 +318,7 @@ bool Instruction::IsVulkanSampledImage() const {
|
||||
|
||||
// Check if the image is sampled. If we know for sure that it is,
|
||||
// then return true.
|
||||
auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
|
||||
return s == 1;
|
||||
return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) == 1;
|
||||
}
|
||||
|
||||
bool Instruction::IsVulkanStorageTexelBuffer() const {
|
||||
@ -502,16 +501,16 @@ uint32_t Instruction::GetTypeComponent(uint32_t element) const {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
|
||||
i.get()->InsertBefore(this);
|
||||
return i.release();
|
||||
Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
|
||||
inst.get()->InsertBefore(this);
|
||||
return inst.release();
|
||||
}
|
||||
|
||||
Instruction* Instruction::InsertBefore(
|
||||
std::vector<std::unique_ptr<Instruction>>&& list) {
|
||||
Instruction* first_node = list.front().get();
|
||||
for (auto& i : list) {
|
||||
i.release()->InsertBefore(this);
|
||||
for (auto& inst : list) {
|
||||
inst.release()->InsertBefore(this);
|
||||
}
|
||||
list.clear();
|
||||
return first_node;
|
||||
@ -568,10 +567,13 @@ bool Instruction::IsValidBasePointer() const {
|
||||
}
|
||||
|
||||
OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
|
||||
if (opcode() != SpvOpExtInst) return OpenCLDebugInfo100InstructionsMax;
|
||||
|
||||
if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo())
|
||||
if (opcode() != SpvOpExtInst) {
|
||||
return OpenCLDebugInfo100InstructionsMax;
|
||||
}
|
||||
|
||||
if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
|
||||
return OpenCLDebugInfo100InstructionsMax;
|
||||
}
|
||||
|
||||
if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
|
||||
context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
|
||||
@ -622,6 +624,7 @@ bool Instruction::IsFoldableByFoldScalar() const {
|
||||
if (!folder.IsFoldableOpcode(opcode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
|
||||
if (!folder.IsFoldableType(type)) {
|
||||
return false;
|
||||
@ -889,6 +892,16 @@ bool Instruction::IsOpcodeSafeToDelete() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Instruction::IsNonSemanticInstruction() const {
|
||||
if (!HasResultId()) return false;
|
||||
if (opcode() != SpvOpExtInst) return false;
|
||||
|
||||
auto import_inst =
|
||||
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0));
|
||||
std::string import_name = import_inst->GetInOperand(0).AsString();
|
||||
return import_name.find("NonSemantic.") == 0;
|
||||
}
|
||||
|
||||
void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
|
||||
uint32_t ext_set,
|
||||
std::vector<uint32_t>* binary) const {
|
||||
|
43
3rdparty/spirv-tools/source/opt/instruction.h
vendored
43
3rdparty/spirv-tools/source/opt/instruction.h
vendored
@ -549,6 +549,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
|
||||
}
|
||||
|
||||
// Returns true if this instructions a non-semantic instruction.
|
||||
bool IsNonSemanticInstruction() const;
|
||||
|
||||
// Dump this instruction on stderr. Useful when running interactive
|
||||
// debuggers.
|
||||
void Dump() const;
|
||||
@ -749,21 +752,21 @@ inline void Instruction::ForEachInst(
|
||||
}
|
||||
|
||||
inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_)
|
||||
if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
|
||||
for (auto& operand : operands_)
|
||||
if (spvIsIdType(operand.type)) f(&operand.words[0]);
|
||||
}
|
||||
|
||||
inline void Instruction::ForEachId(
|
||||
const std::function<void(const uint32_t*)>& f) const {
|
||||
for (const auto& opnd : operands_)
|
||||
if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
|
||||
for (const auto& operand : operands_)
|
||||
if (spvIsIdType(operand.type)) f(&operand.words[0]);
|
||||
}
|
||||
|
||||
inline bool Instruction::WhileEachInId(
|
||||
const std::function<bool(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_) {
|
||||
if (spvIsInIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
for (auto& operand : operands_) {
|
||||
if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -771,9 +774,9 @@ inline bool Instruction::WhileEachInId(
|
||||
|
||||
inline bool Instruction::WhileEachInId(
|
||||
const std::function<bool(const uint32_t*)>& f) const {
|
||||
for (const auto& opnd : operands_) {
|
||||
if (spvIsInIdType(opnd.type)) {
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
for (const auto& operand : operands_) {
|
||||
if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -796,13 +799,13 @@ inline void Instruction::ForEachInId(
|
||||
|
||||
inline bool Instruction::WhileEachInOperand(
|
||||
const std::function<bool(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_) {
|
||||
switch (opnd.type) {
|
||||
for (auto& operand : operands_) {
|
||||
switch (operand.type) {
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
break;
|
||||
default:
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
if (!f(&operand.words[0])) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -811,13 +814,13 @@ inline bool Instruction::WhileEachInOperand(
|
||||
|
||||
inline bool Instruction::WhileEachInOperand(
|
||||
const std::function<bool(const uint32_t*)>& f) const {
|
||||
for (const auto& opnd : operands_) {
|
||||
switch (opnd.type) {
|
||||
for (const auto& operand : operands_) {
|
||||
switch (operand.type) {
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
break;
|
||||
default:
|
||||
if (!f(&opnd.words[0])) return false;
|
||||
if (!f(&operand.words[0])) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -826,16 +829,16 @@ inline bool Instruction::WhileEachInOperand(
|
||||
|
||||
inline void Instruction::ForEachInOperand(
|
||||
const std::function<void(uint32_t*)>& f) {
|
||||
WhileEachInOperand([&f](uint32_t* op) {
|
||||
f(op);
|
||||
WhileEachInOperand([&f](uint32_t* operand) {
|
||||
f(operand);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
inline void Instruction::ForEachInOperand(
|
||||
const std::function<void(const uint32_t*)>& f) const {
|
||||
WhileEachInOperand([&f](const uint32_t* op) {
|
||||
f(op);
|
||||
WhileEachInOperand([&f](const uint32_t* operand) {
|
||||
f(operand);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
24
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
24
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
@ -213,6 +213,30 @@ Instruction* IRContext::KillInst(Instruction* inst) {
|
||||
return next_instruction;
|
||||
}
|
||||
|
||||
void IRContext::KillNonSemanticInfo(Instruction* inst) {
|
||||
if (!inst->HasResultId()) return;
|
||||
std::vector<Instruction*> work_list;
|
||||
std::vector<Instruction*> to_kill;
|
||||
std::unordered_set<Instruction*> seen;
|
||||
work_list.push_back(inst);
|
||||
|
||||
while (!work_list.empty()) {
|
||||
auto* i = work_list.back();
|
||||
work_list.pop_back();
|
||||
get_def_use_mgr()->ForEachUser(
|
||||
i, [&work_list, &to_kill, &seen](Instruction* user) {
|
||||
if (user->IsNonSemanticInstruction() && seen.insert(user).second) {
|
||||
work_list.push_back(user);
|
||||
to_kill.push_back(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto* dead : to_kill) {
|
||||
KillInst(dead);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRContext::KillDef(uint32_t id) {
|
||||
Instruction* def = get_def_use_mgr()->GetDef(id);
|
||||
if (def != nullptr) {
|
||||
|
3
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
3
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@ -403,6 +403,9 @@ class IRContext {
|
||||
// instruction exists.
|
||||
Instruction* KillInst(Instruction* inst);
|
||||
|
||||
// Removes the non-semantic instruction tree that uses |inst|'s result id.
|
||||
void KillNonSemanticInfo(Instruction* inst);
|
||||
|
||||
// Returns true if all of the given analyses are valid.
|
||||
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
|
||||
|
||||
|
15
3rdparty/spirv-tools/source/opt/ir_loader.cpp
vendored
15
3rdparty/spirv-tools/source/opt/ir_loader.cpp
vendored
@ -167,13 +167,22 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
||||
} else if (IsTypeInst(opcode)) {
|
||||
module_->AddType(std::move(spv_inst));
|
||||
} else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
|
||||
opcode == SpvOpUndef ||
|
||||
(opcode == SpvOpExtInst &&
|
||||
spvExtInstIsNonSemantic(inst->ext_inst_type))) {
|
||||
opcode == SpvOpUndef) {
|
||||
module_->AddGlobalValue(std::move(spv_inst));
|
||||
} else if (opcode == SpvOpExtInst &&
|
||||
spvExtInstIsDebugInfo(inst->ext_inst_type)) {
|
||||
module_->AddExtInstDebugInfo(std::move(spv_inst));
|
||||
} else if (opcode == SpvOpExtInst &&
|
||||
spvExtInstIsNonSemantic(inst->ext_inst_type)) {
|
||||
// If there are no functions, add the non-semantic instructions to the
|
||||
// global values. Otherwise append it to the list of the last function.
|
||||
auto func_begin = module_->begin();
|
||||
auto func_end = module_->end();
|
||||
if (func_begin == func_end) {
|
||||
module_->AddGlobalValue(std::move(spv_inst));
|
||||
} else {
|
||||
(--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
|
||||
}
|
||||
} else {
|
||||
Errorf(consumer_, src, loc,
|
||||
"Unhandled inst type (opcode: %d) found outside function "
|
||||
|
@ -77,6 +77,15 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
const Instruction* address_inst, Instruction* original_load) {
|
||||
// Build and append load of variable in ptrInst
|
||||
if (address_inst->NumInOperands() == 1) {
|
||||
// An access chain with no indices is essentially a copy. All that is
|
||||
// needed is to propagate the address.
|
||||
context()->ReplaceAllUsesWith(
|
||||
address_inst->result_id(),
|
||||
address_inst->GetSingleWordInOperand(kAccessChainPtrIdInIdx));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Instruction>> new_inst;
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
@ -109,6 +118,18 @@ bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
|
||||
bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
const Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<Instruction>>* newInsts) {
|
||||
if (ptrInst->NumInOperands() == 1) {
|
||||
// An access chain with no indices is essentially a copy. However, we still
|
||||
// have to create a new store because the old ones will be deleted.
|
||||
BuildAndAppendInst(
|
||||
SpvOpStore, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}},
|
||||
newInsts);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build and append load of variable in ptrInst
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
@ -246,11 +267,13 @@ Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains(
|
||||
if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) {
|
||||
return Status::Failure;
|
||||
}
|
||||
size_t num_of_instructions_to_skip = newInsts.size() - 1;
|
||||
dead_instructions.push_back(&*ii);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
for (size_t i = 0; i < num_of_instructions_to_skip; ++i) {
|
||||
++ii;
|
||||
++ii;
|
||||
}
|
||||
modified = true;
|
||||
} break;
|
||||
default:
|
||||
|
@ -191,14 +191,13 @@ bool Loop::GetInductionInitValue(const Instruction* induction,
|
||||
if (!constant) return false;
|
||||
|
||||
if (value) {
|
||||
const analysis::Integer* type =
|
||||
constant->AsIntConstant()->type()->AsInteger();
|
||||
|
||||
if (type->IsSigned()) {
|
||||
*value = constant->AsIntConstant()->GetS32BitValue();
|
||||
} else {
|
||||
*value = constant->AsIntConstant()->GetU32BitValue();
|
||||
const analysis::Integer* type = constant->type()->AsInteger();
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = type->IsSigned() ? constant->GetSignExtendedValue()
|
||||
: constant->GetZeroExtendedValue();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -682,22 +681,19 @@ bool Loop::FindNumberOfIterations(const Instruction* induction,
|
||||
if (!upper_bound) return false;
|
||||
|
||||
// Must be integer because of the opcode on the condition.
|
||||
int64_t condition_value = 0;
|
||||
const analysis::Integer* type = upper_bound->type()->AsInteger();
|
||||
|
||||
const analysis::Integer* type =
|
||||
upper_bound->AsIntConstant()->type()->AsInteger();
|
||||
|
||||
if (type->width() > 32) {
|
||||
if (!type || type->width() > 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type->IsSigned()) {
|
||||
condition_value = upper_bound->AsIntConstant()->GetS32BitValue();
|
||||
} else {
|
||||
condition_value = upper_bound->AsIntConstant()->GetU32BitValue();
|
||||
}
|
||||
int64_t condition_value = type->IsSigned()
|
||||
? upper_bound->GetSignExtendedValue()
|
||||
: upper_bound->GetZeroExtendedValue();
|
||||
|
||||
// Find the instruction which is stepping through the loop.
|
||||
//
|
||||
// GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull.
|
||||
Instruction* step_inst = GetInductionStepOperation(induction);
|
||||
if (!step_inst) return false;
|
||||
|
||||
|
10
3rdparty/spirv-tools/source/opt/module.cpp
vendored
10
3rdparty/spirv-tools/source/opt/module.cpp
vendored
@ -98,7 +98,10 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
DELEGATE(ext_inst_debuginfo_);
|
||||
DELEGATE(annotations_);
|
||||
DELEGATE(types_values_);
|
||||
for (auto& i : functions_) i->ForEachInst(f, run_on_debug_line_insts);
|
||||
for (auto& i : functions_) {
|
||||
i->ForEachInst(f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
#undef DELEGATE
|
||||
}
|
||||
|
||||
@ -120,8 +123,9 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
for (auto& i : types_values_) DELEGATE(i);
|
||||
for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
|
||||
for (auto& i : functions_) {
|
||||
static_cast<const Function*>(i.get())->ForEachInst(f,
|
||||
run_on_debug_line_insts);
|
||||
static_cast<const Function*>(i.get())->ForEachInst(
|
||||
f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
if (run_on_debug_line_insts) {
|
||||
for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
|
||||
|
@ -70,7 +70,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
simple_conditional_branch_to_branch_reduction_opportunity.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
@ -81,6 +81,7 @@ bool DoesDebugInfoOperandMatchExpectation(
|
||||
const ValidationState_t& _,
|
||||
const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
|
||||
const Instruction* inst, uint32_t word_index) {
|
||||
if (inst->words().size() <= word_index) return false;
|
||||
auto* debug_inst = _.FindDef(inst->word(word_index));
|
||||
if (debug_inst->opcode() != SpvOpExtInst ||
|
||||
debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
|
||||
@ -167,9 +168,16 @@ spv_result_t ValidateOperandLexicalScope(
|
||||
spv_result_t ValidateOperandDebugType(
|
||||
ValidationState_t& _, const std::string& debug_inst_name,
|
||||
const Instruction* inst, uint32_t word_index,
|
||||
const std::function<std::string()>& ext_inst_name) {
|
||||
const std::function<std::string()>& ext_inst_name,
|
||||
bool allow_template_param) {
|
||||
std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
[&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
if (allow_template_param &&
|
||||
(dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter ||
|
||||
dbg_inst ==
|
||||
OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) {
|
||||
return true;
|
||||
}
|
||||
return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
|
||||
dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
|
||||
};
|
||||
@ -636,6 +644,45 @@ spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
|
||||
Instruction* instr) {
|
||||
if (instr->opcode() != SpvOpConstant) return false;
|
||||
if (!_.IsIntScalarType(instr->type_id())) return false;
|
||||
uint32_t size_in_bits = _.GetBitWidth(instr->type_id());
|
||||
return size_in_bits == 32 || size_in_bits == 64;
|
||||
}
|
||||
|
||||
bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst,
|
||||
uint32_t word_index) {
|
||||
auto* int_scalar_const = _.FindDef(inst->word(word_index));
|
||||
if (int_scalar_const->opcode() == SpvOpConstant &&
|
||||
_.IsIntScalarType(int_scalar_const->type_id())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
|
||||
const Instruction* inst,
|
||||
uint32_t word_index) {
|
||||
auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index));
|
||||
if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
|
||||
OpenCLDebugInfo100DebugLocalVariable ||
|
||||
OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
|
||||
OpenCLDebugInfo100DebugGlobalVariable) {
|
||||
auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6));
|
||||
if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) ==
|
||||
OpenCLDebugInfo100DebugTypeBasic &&
|
||||
(OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
|
||||
OpenCLDebugInfo100Signed ||
|
||||
OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
|
||||
OpenCLDebugInfo100Unsigned)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
|
||||
@ -2678,17 +2725,53 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugTypeArray: {
|
||||
auto validate_base_type =
|
||||
ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name);
|
||||
auto validate_base_type = ValidateOperandDebugType(
|
||||
_, "Base Type", inst, 5, ext_inst_name, false);
|
||||
if (validate_base_type != SPV_SUCCESS) return validate_base_type;
|
||||
for (uint32_t i = 6; i < num_words; ++i) {
|
||||
CHECK_OPERAND("Component Count", SpvOpConstant, i);
|
||||
bool invalid = false;
|
||||
auto* component_count = _.FindDef(inst->word(i));
|
||||
if (!_.IsIntScalarType(component_count->type_id()) ||
|
||||
!component_count->word(3)) {
|
||||
if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
|
||||
// TODO: We need a spec discussion for the bindless array.
|
||||
if (!component_count->word(3)) {
|
||||
invalid = true;
|
||||
}
|
||||
} else if (component_count->words().size() > 6 &&
|
||||
(OpenCLDebugInfo100Instructions(component_count->word(
|
||||
4)) == OpenCLDebugInfo100DebugLocalVariable ||
|
||||
OpenCLDebugInfo100Instructions(component_count->word(
|
||||
4)) == OpenCLDebugInfo100DebugGlobalVariable)) {
|
||||
auto* component_count_type = _.FindDef(component_count->word(6));
|
||||
if (component_count_type->words().size() > 7) {
|
||||
if (OpenCLDebugInfo100Instructions(component_count_type->word(
|
||||
4)) != OpenCLDebugInfo100DebugTypeBasic ||
|
||||
OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
|
||||
component_count_type->word(7)) !=
|
||||
OpenCLDebugInfo100Unsigned) {
|
||||
invalid = true;
|
||||
} else {
|
||||
// DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
|
||||
// must have Unsigned encoding and 32 or 64 as its size in bits.
|
||||
Instruction* size_in_bits =
|
||||
_.FindDef(component_count_type->word(6));
|
||||
if (!_.IsIntScalarType(size_in_bits->type_id()) ||
|
||||
(size_in_bits->word(3) != 32 &&
|
||||
size_in_bits->word(3) != 64)) {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
if (invalid) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": Component Count must be positive "
|
||||
<< "integer";
|
||||
<< ext_inst_name() << ": Component Count must be "
|
||||
<< "OpConstant with a 32- or 64-bits integer scalar type or "
|
||||
<< "DebugGlobalVariable or DebugLocalVariable with a 32- or "
|
||||
<< "64-bits unsigned integer scalar type";
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2706,14 +2789,16 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
case OpenCLDebugInfo100DebugTypeFunction: {
|
||||
auto* return_type = _.FindDef(inst->word(6));
|
||||
// TODO: We need a spec discussion that we have to allow return and
|
||||
// parameter types of a DebugTypeFunction to have template parameter.
|
||||
if (return_type->opcode() != SpvOpTypeVoid) {
|
||||
auto validate_return = ValidateOperandDebugType(
|
||||
_, "Return Type", inst, 6, ext_inst_name);
|
||||
_, "Return Type", inst, 6, ext_inst_name, true);
|
||||
if (validate_return != SPV_SUCCESS) return validate_return;
|
||||
}
|
||||
for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
|
||||
auto validate_param = ValidateOperandDebugType(
|
||||
_, "Parameter Types", inst, word_index, ext_inst_name);
|
||||
_, "Parameter Types", inst, word_index, ext_inst_name, true);
|
||||
if (validate_param != SPV_SUCCESS) return validate_param;
|
||||
}
|
||||
break;
|
||||
@ -2727,7 +2812,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
},
|
||||
inst, 6)) {
|
||||
auto validate_underlying_type = ValidateOperandDebugType(
|
||||
_, "Underlying Types", inst, 6, ext_inst_name);
|
||||
_, "Underlying Types", inst, 6, ext_inst_name, false);
|
||||
if (validate_underlying_type != SPV_SUCCESS)
|
||||
return validate_underlying_type;
|
||||
}
|
||||
@ -2784,8 +2869,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
case OpenCLDebugInfo100DebugTypeMember: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
// TODO: We need a spec discussion that we have to allow member types
|
||||
// to have template parameter.
|
||||
auto validate_type =
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
|
||||
if (validate_type != SPV_SUCCESS) return validate_type;
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
|
||||
CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
|
||||
@ -2823,18 +2910,13 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
case OpenCLDebugInfo100DebugFunction: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
auto validate_type =
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
|
||||
if (validate_type != SPV_SUCCESS) return validate_type;
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
|
||||
auto validate_parent =
|
||||
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
|
||||
if (validate_parent != SPV_SUCCESS) return validate_parent;
|
||||
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
|
||||
// TODO: The current OpenCL.100.DebugInfo spec says "Function
|
||||
// is an OpFunction which is described by this instruction.".
|
||||
// However, the function definition can be opted-out e.g.,
|
||||
// inlining. We assume that Function operand can be a
|
||||
// DebugInfoNone, but we must discuss it and update the spec.
|
||||
if (!DoesDebugInfoOperandMatchExpectation(
|
||||
_,
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
@ -2852,7 +2934,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
case OpenCLDebugInfo100DebugFunctionDeclaration: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
auto validate_type =
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
|
||||
if (validate_type != SPV_SUCCESS) return validate_type;
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
|
||||
auto validate_parent =
|
||||
@ -2870,9 +2952,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugScope: {
|
||||
// TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are
|
||||
// still in spec discussion about what must be "Scope" operand of
|
||||
// DebugScope. Update this code if the conclusion is different.
|
||||
auto validate_scope =
|
||||
ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
|
||||
if (validate_scope != SPV_SUCCESS) return validate_scope;
|
||||
@ -2884,8 +2963,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
case OpenCLDebugInfo100DebugLocalVariable: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
// TODO: We need a spec discussion that we have to allow local variable
|
||||
// types to have template parameter.
|
||||
auto validate_type =
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
|
||||
if (validate_type != SPV_SUCCESS) return validate_type;
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
|
||||
auto validate_parent =
|
||||
@ -2896,11 +2977,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
case OpenCLDebugInfo100DebugDeclare: {
|
||||
CHECK_DEBUG_OPERAND("Local Variable",
|
||||
OpenCLDebugInfo100DebugLocalVariable, 5);
|
||||
|
||||
// TODO: We must discuss DebugDeclare.Variable of
|
||||
// OpenCL.100.DebugInfo. Currently, it says "Variable must be an id of
|
||||
// OpVariable instruction which defines the local variable.", but we
|
||||
// want to allow OpFunctionParameter as well.
|
||||
auto* operand = _.FindDef(inst->word(6));
|
||||
if (operand->opcode() != SpvOpVariable &&
|
||||
operand->opcode() != SpvOpFunctionParameter) {
|
||||
@ -2920,18 +2996,120 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugTypeTemplate: {
|
||||
if (!DoesDebugInfoOperandMatchExpectation(
|
||||
_,
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
return dbg_inst == OpenCLDebugInfo100DebugTypeComposite ||
|
||||
dbg_inst == OpenCLDebugInfo100DebugFunction;
|
||||
},
|
||||
inst, 5)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected operand Target must be DebugTypeComposite "
|
||||
<< "or DebugFunction";
|
||||
}
|
||||
for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
|
||||
if (!DoesDebugInfoOperandMatchExpectation(
|
||||
_,
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
return dbg_inst ==
|
||||
OpenCLDebugInfo100DebugTypeTemplateParameter ||
|
||||
dbg_inst ==
|
||||
OpenCLDebugInfo100DebugTypeTemplateTemplateParameter;
|
||||
},
|
||||
inst, word_index)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected operand Parameters must be "
|
||||
<< "DebugTypeTemplateParameter or "
|
||||
<< "DebugTypeTemplateTemplateParameter";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugTypeTemplateParameter: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
auto validate_actual_type = ValidateOperandDebugType(
|
||||
_, "Actual Type", inst, 6, ext_inst_name, false);
|
||||
if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
|
||||
if (!DoesDebugInfoOperandMatchExpectation(
|
||||
_,
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
|
||||
},
|
||||
inst, 7)) {
|
||||
CHECK_OPERAND("Value", SpvOpConstant, 7);
|
||||
}
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8);
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugGlobalVariable: {
|
||||
CHECK_OPERAND("Name", SpvOpString, 5);
|
||||
auto validate_type =
|
||||
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
|
||||
if (validate_type != SPV_SUCCESS) return validate_type;
|
||||
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
|
||||
auto validate_scope =
|
||||
ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
|
||||
if (validate_scope != SPV_SUCCESS) return validate_scope;
|
||||
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
|
||||
if (!DoesDebugInfoOperandMatchExpectation(
|
||||
_,
|
||||
[](OpenCLDebugInfo100Instructions dbg_inst) {
|
||||
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
|
||||
},
|
||||
inst, 12)) {
|
||||
auto* operand = _.FindDef(inst->word(12));
|
||||
if (operand->opcode() != SpvOpVariable &&
|
||||
operand->opcode() != SpvOpConstant) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": "
|
||||
<< "expected operand Variable must be a result id of "
|
||||
"OpVariable or OpConstant or DebugInfoNone";
|
||||
}
|
||||
}
|
||||
if (num_words == 15) {
|
||||
CHECK_DEBUG_OPERAND("Static Member Declaration",
|
||||
OpenCLDebugInfo100DebugTypeMember, 14);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugInlinedAt: {
|
||||
auto validate_scope =
|
||||
ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
|
||||
if (validate_scope != SPV_SUCCESS) return validate_scope;
|
||||
if (num_words == 8) {
|
||||
CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenCLDebugInfo100DebugValue: {
|
||||
CHECK_DEBUG_OPERAND("Local Variable",
|
||||
OpenCLDebugInfo100DebugLocalVariable, 5);
|
||||
CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
|
||||
|
||||
for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
|
||||
// TODO: The following code simply checks if it is a const int scalar
|
||||
// or a DebugLocalVariable or DebugGlobalVariable, but we have to
|
||||
// check it using the same validation for Indexes of OpAccessChain.
|
||||
if (!IsConstWithIntScalarType(_, inst, word_index) &&
|
||||
!IsDebugVariableWithIntScalarType(_, inst, word_index)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< ext_inst_name() << ": expected operand Indexes is "
|
||||
<< "OpConstant, DebugGlobalVariable, or "
|
||||
<< "type is OpConstant with an integer scalar type";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Add validation rules for remaining cases as well.
|
||||
case OpenCLDebugInfo100DebugTypePtrToMember:
|
||||
case OpenCLDebugInfo100DebugTypeTemplate:
|
||||
case OpenCLDebugInfo100DebugTypeTemplateParameter:
|
||||
case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
|
||||
case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
|
||||
case OpenCLDebugInfo100DebugGlobalVariable:
|
||||
case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
|
||||
case OpenCLDebugInfo100DebugInlinedAt:
|
||||
case OpenCLDebugInfo100DebugInlinedVariable:
|
||||
case OpenCLDebugInfo100DebugValue:
|
||||
case OpenCLDebugInfo100DebugMacroDef:
|
||||
case OpenCLDebugInfo100DebugMacroUndef:
|
||||
case OpenCLDebugInfo100DebugImportedEntity:
|
||||
|
@ -437,6 +437,19 @@ spv_result_t GetLocationsForVariable(
|
||||
|
||||
spv_result_t ValidateLocations(ValidationState_t& _,
|
||||
const Instruction* entry_point) {
|
||||
// According to Vulkan 14.1 only the following execution models have
|
||||
// locations assigned.
|
||||
switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
|
||||
case SpvExecutionModelVertex:
|
||||
case SpvExecutionModelTessellationControl:
|
||||
case SpvExecutionModelTessellationEvaluation:
|
||||
case SpvExecutionModelGeometry:
|
||||
case SpvExecutionModelFragment:
|
||||
break;
|
||||
default:
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Locations are stored as a combined location and component values.
|
||||
std::unordered_set<uint32_t> input_locations;
|
||||
std::unordered_set<uint32_t> output_locations_index0;
|
||||
|
Loading…
Reference in New Issue
Block a user