mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-18 04:53:06 +01:00
Updated spirv-tools.
This commit is contained in:
@@ -1 +1 @@
|
||||
"v2020.5", "SPIRV-Tools v2020.5 e272a31e19b278158551bc64142947df09ede4ac"
|
||||
"v2020.5", "SPIRV-Tools v2020.5 86402fec24134b6d4103b6ad1e689fb5840f720f"
|
||||
|
||||
392
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
392
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@@ -1,392 +0,0 @@
|
||||
# Copyright (c) 2019 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.
|
||||
|
||||
if(SPIRV_BUILD_FUZZER)
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobufs)
|
||||
|
||||
set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT protobufs/spvtoolsfuzz.pb.cc protobufs/spvtoolsfuzz.pb.h
|
||||
COMMAND protobuf::protoc
|
||||
-I=${CMAKE_CURRENT_SOURCE_DIR}/protobufs
|
||||
--cpp_out=protobufs
|
||||
${PROTOBUF_SOURCE}
|
||||
DEPENDS ${PROTOBUF_SOURCE}
|
||||
COMMENT "Generate protobuf sources from proto definition file."
|
||||
)
|
||||
|
||||
set(SPIRV_TOOLS_FUZZ_SOURCES
|
||||
call_graph.h
|
||||
counter_overflow_id_source.h
|
||||
data_descriptor.h
|
||||
equivalence_relation.h
|
||||
fact_manager/constant_uniform_facts.h
|
||||
fact_manager/data_synonym_and_id_equation_facts.h
|
||||
fact_manager/dead_block_facts.h
|
||||
fact_manager/fact_manager.h
|
||||
fact_manager/irrelevant_value_facts.h
|
||||
fact_manager/livesafe_function_facts.h
|
||||
force_render_red.h
|
||||
fuzzer.h
|
||||
fuzzer_context.h
|
||||
fuzzer_pass.h
|
||||
fuzzer_pass_add_access_chains.h
|
||||
fuzzer_pass_add_composite_inserts.h
|
||||
fuzzer_pass_add_composite_types.h
|
||||
fuzzer_pass_add_copy_memory.h
|
||||
fuzzer_pass_add_dead_blocks.h
|
||||
fuzzer_pass_add_dead_breaks.h
|
||||
fuzzer_pass_add_dead_continues.h
|
||||
fuzzer_pass_add_equation_instructions.h
|
||||
fuzzer_pass_add_function_calls.h
|
||||
fuzzer_pass_add_global_variables.h
|
||||
fuzzer_pass_add_image_sample_unused_components.h
|
||||
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_opphi_synonyms.h
|
||||
fuzzer_pass_add_parameters.h
|
||||
fuzzer_pass_add_relaxed_decorations.h
|
||||
fuzzer_pass_add_stores.h
|
||||
fuzzer_pass_add_vector_shuffle_instructions.h
|
||||
fuzzer_pass_adjust_branch_weights.h
|
||||
fuzzer_pass_adjust_function_controls.h
|
||||
fuzzer_pass_adjust_loop_controls.h
|
||||
fuzzer_pass_adjust_memory_operands_masks.h
|
||||
fuzzer_pass_adjust_selection_controls.h
|
||||
fuzzer_pass_apply_id_synonyms.h
|
||||
fuzzer_pass_construct_composites.h
|
||||
fuzzer_pass_copy_objects.h
|
||||
fuzzer_pass_donate_modules.h
|
||||
fuzzer_pass_inline_functions.h
|
||||
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
|
||||
fuzzer_pass_replace_loads_stores_with_copy_memories.h
|
||||
fuzzer_pass_replace_parameter_with_global.h
|
||||
fuzzer_pass_replace_params_with_struct.h
|
||||
fuzzer_pass_split_blocks.h
|
||||
fuzzer_pass_swap_commutable_operands.h
|
||||
fuzzer_pass_swap_conditional_branch_operands.h
|
||||
fuzzer_pass_toggle_access_chain_instruction.h
|
||||
fuzzer_util.h
|
||||
id_use_descriptor.h
|
||||
instruction_descriptor.h
|
||||
instruction_message.h
|
||||
overflow_id_source.h
|
||||
protobufs/spirvfuzz_protobufs.h
|
||||
pseudo_random_generator.h
|
||||
random_generator.h
|
||||
replayer.h
|
||||
shrinker.h
|
||||
transformation.h
|
||||
transformation_access_chain.h
|
||||
transformation_add_constant_boolean.h
|
||||
transformation_add_constant_composite.h
|
||||
transformation_add_constant_null.h
|
||||
transformation_add_constant_scalar.h
|
||||
transformation_add_copy_memory.h
|
||||
transformation_add_dead_block.h
|
||||
transformation_add_dead_break.h
|
||||
transformation_add_dead_continue.h
|
||||
transformation_add_function.h
|
||||
transformation_add_global_undef.h
|
||||
transformation_add_global_variable.h
|
||||
transformation_add_image_sample_unused_components.h
|
||||
transformation_add_local_variable.h
|
||||
transformation_add_loop_preheader.h
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_opphi_synonym.h
|
||||
transformation_add_parameter.h
|
||||
transformation_add_relaxed_decoration.h
|
||||
transformation_add_spec_constant_op.h
|
||||
transformation_add_synonym.h
|
||||
transformation_add_type_array.h
|
||||
transformation_add_type_boolean.h
|
||||
transformation_add_type_float.h
|
||||
transformation_add_type_function.h
|
||||
transformation_add_type_int.h
|
||||
transformation_add_type_matrix.h
|
||||
transformation_add_type_pointer.h
|
||||
transformation_add_type_struct.h
|
||||
transformation_add_type_vector.h
|
||||
transformation_adjust_branch_weights.h
|
||||
transformation_composite_construct.h
|
||||
transformation_composite_extract.h
|
||||
transformation_composite_insert.h
|
||||
transformation_compute_data_synonym_fact_closure.h
|
||||
transformation_context.h
|
||||
transformation_equation_instruction.h
|
||||
transformation_function_call.h
|
||||
transformation_inline_function.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
|
||||
transformation_replace_copy_object_with_store_load.h
|
||||
transformation_replace_id_with_synonym.h
|
||||
transformation_replace_linear_algebra_instruction.h
|
||||
transformation_replace_load_store_with_copy_memory.h
|
||||
transformation_replace_parameter_with_global.h
|
||||
transformation_replace_params_with_struct.h
|
||||
transformation_set_function_control.h
|
||||
transformation_set_loop_control.h
|
||||
transformation_set_memory_operands_mask.h
|
||||
transformation_set_selection_control.h
|
||||
transformation_split_block.h
|
||||
transformation_store.h
|
||||
transformation_swap_commutable_operands.h
|
||||
transformation_swap_conditional_branch_operands.h
|
||||
transformation_toggle_access_chain_instruction.h
|
||||
transformation_vector_shuffle.h
|
||||
uniform_buffer_element_descriptor.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
|
||||
|
||||
call_graph.cpp
|
||||
counter_overflow_id_source.cpp
|
||||
data_descriptor.cpp
|
||||
fact_manager/constant_uniform_facts.cpp
|
||||
fact_manager/data_synonym_and_id_equation_facts.cpp
|
||||
fact_manager/dead_block_facts.cpp
|
||||
fact_manager/fact_manager.cpp
|
||||
fact_manager/irrelevant_value_facts.cpp
|
||||
fact_manager/livesafe_function_facts.cpp
|
||||
force_render_red.cpp
|
||||
fuzzer.cpp
|
||||
fuzzer_context.cpp
|
||||
fuzzer_pass.cpp
|
||||
fuzzer_pass_add_access_chains.cpp
|
||||
fuzzer_pass_add_composite_inserts.cpp
|
||||
fuzzer_pass_add_composite_types.cpp
|
||||
fuzzer_pass_add_copy_memory.cpp
|
||||
fuzzer_pass_add_dead_blocks.cpp
|
||||
fuzzer_pass_add_dead_breaks.cpp
|
||||
fuzzer_pass_add_dead_continues.cpp
|
||||
fuzzer_pass_add_equation_instructions.cpp
|
||||
fuzzer_pass_add_function_calls.cpp
|
||||
fuzzer_pass_add_global_variables.cpp
|
||||
fuzzer_pass_add_image_sample_unused_components.cpp
|
||||
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_opphi_synonyms.cpp
|
||||
fuzzer_pass_add_parameters.cpp
|
||||
fuzzer_pass_add_relaxed_decorations.cpp
|
||||
fuzzer_pass_add_stores.cpp
|
||||
fuzzer_pass_add_vector_shuffle_instructions.cpp
|
||||
fuzzer_pass_adjust_branch_weights.cpp
|
||||
fuzzer_pass_adjust_function_controls.cpp
|
||||
fuzzer_pass_adjust_loop_controls.cpp
|
||||
fuzzer_pass_adjust_memory_operands_masks.cpp
|
||||
fuzzer_pass_adjust_selection_controls.cpp
|
||||
fuzzer_pass_apply_id_synonyms.cpp
|
||||
fuzzer_pass_construct_composites.cpp
|
||||
fuzzer_pass_copy_objects.cpp
|
||||
fuzzer_pass_donate_modules.cpp
|
||||
fuzzer_pass_inline_functions.cpp
|
||||
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
|
||||
fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
|
||||
fuzzer_pass_replace_parameter_with_global.cpp
|
||||
fuzzer_pass_replace_params_with_struct.cpp
|
||||
fuzzer_pass_split_blocks.cpp
|
||||
fuzzer_pass_swap_commutable_operands.cpp
|
||||
fuzzer_pass_swap_conditional_branch_operands.cpp
|
||||
fuzzer_pass_toggle_access_chain_instruction.cpp
|
||||
fuzzer_util.cpp
|
||||
id_use_descriptor.cpp
|
||||
instruction_descriptor.cpp
|
||||
instruction_message.cpp
|
||||
overflow_id_source.cpp
|
||||
pseudo_random_generator.cpp
|
||||
random_generator.cpp
|
||||
replayer.cpp
|
||||
shrinker.cpp
|
||||
transformation.cpp
|
||||
transformation_access_chain.cpp
|
||||
transformation_add_constant_boolean.cpp
|
||||
transformation_add_constant_composite.cpp
|
||||
transformation_add_constant_null.cpp
|
||||
transformation_add_constant_scalar.cpp
|
||||
transformation_add_copy_memory.cpp
|
||||
transformation_add_dead_block.cpp
|
||||
transformation_add_dead_break.cpp
|
||||
transformation_add_dead_continue.cpp
|
||||
transformation_add_function.cpp
|
||||
transformation_add_global_undef.cpp
|
||||
transformation_add_global_variable.cpp
|
||||
transformation_add_image_sample_unused_components.cpp
|
||||
transformation_add_local_variable.cpp
|
||||
transformation_add_loop_preheader.cpp
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_opphi_synonym.cpp
|
||||
transformation_add_parameter.cpp
|
||||
transformation_add_relaxed_decoration.cpp
|
||||
transformation_add_spec_constant_op.cpp
|
||||
transformation_add_synonym.cpp
|
||||
transformation_add_type_array.cpp
|
||||
transformation_add_type_boolean.cpp
|
||||
transformation_add_type_float.cpp
|
||||
transformation_add_type_function.cpp
|
||||
transformation_add_type_int.cpp
|
||||
transformation_add_type_matrix.cpp
|
||||
transformation_add_type_pointer.cpp
|
||||
transformation_add_type_struct.cpp
|
||||
transformation_add_type_vector.cpp
|
||||
transformation_adjust_branch_weights.cpp
|
||||
transformation_composite_construct.cpp
|
||||
transformation_composite_extract.cpp
|
||||
transformation_composite_insert.cpp
|
||||
transformation_compute_data_synonym_fact_closure.cpp
|
||||
transformation_context.cpp
|
||||
transformation_equation_instruction.cpp
|
||||
transformation_function_call.cpp
|
||||
transformation_inline_function.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
|
||||
transformation_replace_copy_object_with_store_load.cpp
|
||||
transformation_replace_id_with_synonym.cpp
|
||||
transformation_replace_linear_algebra_instruction.cpp
|
||||
transformation_replace_load_store_with_copy_memory.cpp
|
||||
transformation_replace_parameter_with_global.cpp
|
||||
transformation_replace_params_with_struct.cpp
|
||||
transformation_set_function_control.cpp
|
||||
transformation_set_loop_control.cpp
|
||||
transformation_set_memory_operands_mask.cpp
|
||||
transformation_set_selection_control.cpp
|
||||
transformation_split_block.cpp
|
||||
transformation_store.cpp
|
||||
transformation_swap_commutable_operands.cpp
|
||||
transformation_swap_conditional_branch_operands.cpp
|
||||
transformation_toggle_access_chain_instruction.cpp
|
||||
transformation_vector_shuffle.cpp
|
||||
uniform_buffer_element_descriptor.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
||||
)
|
||||
|
||||
if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
# Enable parallel builds across four cores for this lib
|
||||
add_definitions(/MP4)
|
||||
endif()
|
||||
|
||||
spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz)
|
||||
|
||||
add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES})
|
||||
|
||||
spvtools_default_compile_options(SPIRV-Tools-fuzz)
|
||||
target_compile_definitions(SPIRV-Tools-fuzz PUBLIC -DGOOGLE_PROTOBUF_NO_RTTI -DGOOGLE_PROTOBUF_USE_UNALIGNED=0)
|
||||
|
||||
# Compilation of the auto-generated protobuf source file will yield warnings,
|
||||
# which we have no control over and thus wish to ignore.
|
||||
if(${COMPILER_IS_LIKE_GNU})
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS -w)
|
||||
endif()
|
||||
if(MSVC)
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS /w)
|
||||
endif()
|
||||
|
||||
target_include_directories(SPIRV-Tools-fuzz
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
PRIVATE ${spirv-tools_BINARY_DIR}
|
||||
PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
||||
# The fuzzer reuses a lot of functionality from the SPIRV-Tools library.
|
||||
target_link_libraries(SPIRV-Tools-fuzz
|
||||
PUBLIC ${SPIRV_TOOLS}-static
|
||||
PUBLIC SPIRV-Tools-opt
|
||||
PUBLIC protobuf::libprotobuf)
|
||||
|
||||
set_property(TARGET SPIRV-Tools-fuzz PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
spvtools_check_symbol_exports(SPIRV-Tools-fuzz)
|
||||
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake)
|
||||
|
||||
spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR)
|
||||
install(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake
|
||||
DESTINATION ${PACKAGE_DIR})
|
||||
|
||||
spvtools_generate_config_file(SPIRV-Tools-fuzz)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-fuzzConfig.cmake DESTINATION ${PACKAGE_DIR})
|
||||
endif(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
|
||||
endif(SPIRV_BUILD_FUZZER)
|
||||
81
3rdparty/spirv-tools/source/fuzz/call_graph.cpp
vendored
81
3rdparty/spirv-tools/source/fuzz/call_graph.cpp
vendored
@@ -1,81 +0,0 @@
|
||||
// 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/call_graph.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
CallGraph::CallGraph(opt::IRContext* context) {
|
||||
// Initialize function in-degree and call graph edges to 0 and empty.
|
||||
for (auto& function : *context->module()) {
|
||||
function_in_degree_[function.result_id()] = 0;
|
||||
call_graph_edges_[function.result_id()] = std::set<uint32_t>();
|
||||
}
|
||||
|
||||
// Consider every function.
|
||||
for (auto& function : *context->module()) {
|
||||
// Avoid considering the same callee of this function multiple times by
|
||||
// recording known callees.
|
||||
std::set<uint32_t> known_callees;
|
||||
// Consider every function call instruction in every block.
|
||||
for (auto& block : function) {
|
||||
for (auto& instruction : block) {
|
||||
if (instruction.opcode() != SpvOpFunctionCall) {
|
||||
continue;
|
||||
}
|
||||
// Get the id of the function being called.
|
||||
uint32_t callee = instruction.GetSingleWordInOperand(0);
|
||||
if (known_callees.count(callee)) {
|
||||
// We have already considered a call to this function - ignore it.
|
||||
continue;
|
||||
}
|
||||
// Increase the callee's in-degree and add an edge to the call graph.
|
||||
function_in_degree_[callee]++;
|
||||
call_graph_edges_[function.result_id()].insert(callee);
|
||||
// Mark the callee as 'known'.
|
||||
known_callees.insert(callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CallGraph::PushDirectCallees(uint32_t function_id,
|
||||
std::queue<uint32_t>* queue) const {
|
||||
for (auto callee : GetDirectCallees(function_id)) {
|
||||
queue->push(callee);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<uint32_t> CallGraph::GetIndirectCallees(uint32_t function_id) const {
|
||||
std::set<uint32_t> result;
|
||||
std::queue<uint32_t> queue;
|
||||
PushDirectCallees(function_id, &queue);
|
||||
|
||||
while (!queue.empty()) {
|
||||
auto next = queue.front();
|
||||
queue.pop();
|
||||
if (result.count(next)) {
|
||||
continue;
|
||||
}
|
||||
result.insert(next);
|
||||
PushDirectCallees(next, &queue);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
62
3rdparty/spirv-tools/source/fuzz/call_graph.h
vendored
62
3rdparty/spirv-tools/source/fuzz/call_graph.h
vendored
@@ -1,62 +0,0 @@
|
||||
// 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_CALL_GRAPH_H_
|
||||
#define SOURCE_FUZZ_CALL_GRAPH_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Represents the acyclic call graph of a SPIR-V module.
|
||||
class CallGraph {
|
||||
public:
|
||||
// Creates a call graph corresponding to the given SPIR-V module.
|
||||
explicit CallGraph(opt::IRContext* context);
|
||||
|
||||
// Returns a mapping from each function to its number of distinct callers.
|
||||
const std::map<uint32_t, uint32_t>& GetFunctionInDegree() const {
|
||||
return function_in_degree_;
|
||||
}
|
||||
|
||||
// Returns the ids of the functions that |function_id| directly invokes.
|
||||
const std::set<uint32_t>& GetDirectCallees(uint32_t function_id) const {
|
||||
return call_graph_edges_.at(function_id);
|
||||
}
|
||||
|
||||
// Returns the ids of the functions that |function_id| directly or indirectly
|
||||
// invokes.
|
||||
std::set<uint32_t> GetIndirectCallees(uint32_t function_id) const;
|
||||
|
||||
private:
|
||||
// Pushes the direct callees of |function_id| on to |queue|.
|
||||
void PushDirectCallees(uint32_t function_id,
|
||||
std::queue<uint32_t>* queue) const;
|
||||
|
||||
// Maps each function id to the ids of its immediate callees.
|
||||
std::map<uint32_t, std::set<uint32_t>> call_graph_edges_;
|
||||
|
||||
// For each function id, stores the number of distinct functions that call
|
||||
// the function.
|
||||
std::map<uint32_t, uint32_t> function_in_degree_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_CALL_GRAPH_H_
|
||||
@@ -1,30 +0,0 @@
|
||||
// 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/counter_overflow_id_source.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
CounterOverflowIdSource::CounterOverflowIdSource(uint32_t first_available_id)
|
||||
: next_available_id_(first_available_id) {}
|
||||
|
||||
bool CounterOverflowIdSource::HasOverflowIds() const { return true; }
|
||||
|
||||
uint32_t CounterOverflowIdSource::GetNextOverflowId() {
|
||||
return next_available_id_++;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,45 +0,0 @@
|
||||
// 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_COUNTER_OVERFLOW_ID_SOURCE_H_
|
||||
#define SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_
|
||||
|
||||
#include "source/fuzz/overflow_id_source.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A source of overflow ids that uses a counter to provide successive ids from
|
||||
// a given starting value.
|
||||
class CounterOverflowIdSource : public OverflowIdSource {
|
||||
public:
|
||||
// |first_available_id| is the starting value for the counter.
|
||||
explicit CounterOverflowIdSource(uint32_t first_available_id);
|
||||
|
||||
// Always returns true.
|
||||
bool HasOverflowIds() const override;
|
||||
|
||||
// Returns the current counter value and increments the counter.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) We should
|
||||
// account for the case where the maximum allowed id is reached.
|
||||
uint32_t GetNextOverflowId() override;
|
||||
|
||||
private:
|
||||
uint32_t next_available_id_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_COUNTER_H_
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright (c) 2019 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/data_descriptor.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
protobufs::DataDescriptor MakeDataDescriptor(uint32_t object,
|
||||
std::vector<uint32_t>&& indices) {
|
||||
protobufs::DataDescriptor result;
|
||||
result.set_object(object);
|
||||
for (auto index : indices) {
|
||||
result.add_index(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t DataDescriptorHash::operator()(
|
||||
const protobufs::DataDescriptor* data_descriptor) const {
|
||||
std::u32string hash;
|
||||
hash.push_back(data_descriptor->object());
|
||||
for (auto an_index : data_descriptor->index()) {
|
||||
hash.push_back(an_index);
|
||||
}
|
||||
return std::hash<std::u32string>()(hash);
|
||||
}
|
||||
|
||||
bool DataDescriptorEquals::operator()(
|
||||
const protobufs::DataDescriptor* first,
|
||||
const protobufs::DataDescriptor* second) const {
|
||||
return first->object() == second->object() &&
|
||||
first->index().size() == second->index().size() &&
|
||||
std::equal(first->index().begin(), first->index().end(),
|
||||
second->index().begin());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const protobufs::DataDescriptor& data_descriptor) {
|
||||
out << data_descriptor.object();
|
||||
out << "[";
|
||||
bool first = true;
|
||||
for (auto index : data_descriptor.index()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << ", ";
|
||||
}
|
||||
out << index;
|
||||
}
|
||||
out << "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) 2019 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_DATA_DESCRIPTOR_H_
|
||||
#define SOURCE_FUZZ_DATA_DESCRIPTOR_H_
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Factory method to create a data descriptor message from an object id and a
|
||||
// list of indices.
|
||||
protobufs::DataDescriptor MakeDataDescriptor(uint32_t object,
|
||||
std::vector<uint32_t>&& indices);
|
||||
|
||||
// Hash function for data descriptors.
|
||||
struct DataDescriptorHash {
|
||||
size_t operator()(const protobufs::DataDescriptor* data_descriptor) const;
|
||||
};
|
||||
|
||||
// Equality function for data descriptors.
|
||||
struct DataDescriptorEquals {
|
||||
bool operator()(const protobufs::DataDescriptor* first,
|
||||
const protobufs::DataDescriptor* second) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const protobufs::DataDescriptor& data_descriptor);
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_DATA_DESCRIPTOR_H_
|
||||
@@ -1,245 +0,0 @@
|
||||
// Copyright (c) 2019 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_EQUIVALENCE_RELATION_H_
|
||||
#define SOURCE_FUZZ_EQUIVALENCE_RELATION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A class for representing an equivalence relation on objects of type |T|,
|
||||
// which should be a value type. The type |T| is required to have a copy
|
||||
// constructor, and |PointerHashT| and |PointerEqualsT| must be functors
|
||||
// providing hashing and equality testing functionality for pointers to objects
|
||||
// of type |T|.
|
||||
//
|
||||
// A disjoint-set (a.k.a. union-find or merge-find) data structure is used to
|
||||
// represent the equivalence relation. Path compression is used. Union by
|
||||
// rank/size is not used.
|
||||
//
|
||||
// Each disjoint set is represented as a tree, rooted at the representative
|
||||
// of the set.
|
||||
//
|
||||
// Getting the representative of a value simply requires chasing parent pointers
|
||||
// from the value until you reach the root.
|
||||
//
|
||||
// Checking equivalence of two elements requires checking that the
|
||||
// representatives are equal.
|
||||
//
|
||||
// Traversing the tree rooted at a value's representative visits the value's
|
||||
// equivalence class.
|
||||
//
|
||||
// |PointerHashT| and |PointerEqualsT| are used to define *equality* between
|
||||
// values, and otherwise are *not* used to define the equivalence relation
|
||||
// (except that equal values are equivalent). The equivalence relation is
|
||||
// constructed by repeatedly adding pairs of (typically non-equal) values that
|
||||
// are deemed to be equivalent.
|
||||
//
|
||||
// For example in an equivalence relation on integers, 1 and 5 might be added
|
||||
// as equivalent, so that IsEquivalent(1, 5) holds, because they represent
|
||||
// IDs in a SPIR-V binary that are known to contain the same value at run time,
|
||||
// but clearly 1 != 5. Since 1 and 1 are equal, IsEquivalent(1, 1) will also
|
||||
// hold.
|
||||
//
|
||||
// Each unique (up to equality) value added to the relation is copied into
|
||||
// |owned_values_|, so there is one canonical memory address per unique value.
|
||||
// Uniqueness is ensured by storing (and checking) a set of pointers to these
|
||||
// values in |value_set_|, which uses |PointerHashT| and |PointerEqualsT|.
|
||||
//
|
||||
// |parent_| and |children_| encode the equivalence relation, i.e., the trees.
|
||||
template <typename T, typename PointerHashT, typename PointerEqualsT>
|
||||
class EquivalenceRelation {
|
||||
public:
|
||||
// Requires that |value1| and |value2| are already registered in the
|
||||
// equivalence relation. Merges the equivalence classes associated with
|
||||
// |value1| and |value2|.
|
||||
void MakeEquivalent(const T& value1, const T& value2) {
|
||||
assert(Exists(value1) &&
|
||||
"Precondition: value1 must already be registered.");
|
||||
assert(Exists(value2) &&
|
||||
"Precondition: value2 must already be registered.");
|
||||
|
||||
// Look up canonical pointers to each of the values in the value pool.
|
||||
const T* value1_ptr = *value_set_.find(&value1);
|
||||
const T* value2_ptr = *value_set_.find(&value2);
|
||||
|
||||
// If the values turn out to be identical, they are already in the same
|
||||
// equivalence class so there is nothing to do.
|
||||
if (value1_ptr == value2_ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the representative for each value's equivalence class, and if they
|
||||
// are not already in the same class, make one the parent of the other.
|
||||
const T* representative1 = Find(value1_ptr);
|
||||
const T* representative2 = Find(value2_ptr);
|
||||
assert(representative1 && "Representatives should never be null.");
|
||||
assert(representative2 && "Representatives should never be null.");
|
||||
if (representative1 != representative2) {
|
||||
parent_[representative1] = representative2;
|
||||
children_[representative2].push_back(representative1);
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that |value| is not known to the equivalence relation. Registers
|
||||
// it in its own equivalence class and returns a pointer to the equivalence
|
||||
// class representative.
|
||||
const T* Register(const T& value) {
|
||||
assert(!Exists(value));
|
||||
|
||||
// This relies on T having a copy constructor.
|
||||
auto unique_pointer_to_value = MakeUnique<T>(value);
|
||||
auto pointer_to_value = unique_pointer_to_value.get();
|
||||
owned_values_.push_back(std::move(unique_pointer_to_value));
|
||||
value_set_.insert(pointer_to_value);
|
||||
|
||||
// Initially say that the value is its own parent and that it has no
|
||||
// children.
|
||||
assert(pointer_to_value && "Representatives should never be null.");
|
||||
parent_[pointer_to_value] = pointer_to_value;
|
||||
children_[pointer_to_value] = std::vector<const T*>();
|
||||
|
||||
return pointer_to_value;
|
||||
}
|
||||
|
||||
// Returns exactly one representative per equivalence class.
|
||||
std::vector<const T*> GetEquivalenceClassRepresentatives() const {
|
||||
std::vector<const T*> result;
|
||||
for (auto& value : owned_values_) {
|
||||
if (parent_[value.get()] == value.get()) {
|
||||
result.push_back(value.get());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns pointers to all values in the equivalence class of |value|, which
|
||||
// must already be part of the equivalence relation.
|
||||
std::vector<const T*> GetEquivalenceClass(const T& value) const {
|
||||
assert(Exists(value));
|
||||
|
||||
std::vector<const T*> result;
|
||||
|
||||
// Traverse the tree of values rooted at the representative of the
|
||||
// equivalence class to which |value| belongs, and collect up all the values
|
||||
// that are encountered. This constitutes the whole equivalence class.
|
||||
std::vector<const T*> stack;
|
||||
stack.push_back(Find(*value_set_.find(&value)));
|
||||
while (!stack.empty()) {
|
||||
const T* item = stack.back();
|
||||
result.push_back(item);
|
||||
stack.pop_back();
|
||||
for (auto child : children_[item]) {
|
||||
stack.push_back(child);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if and only if |value1| and |value2| are in the same
|
||||
// equivalence class. Both values must already be known to the equivalence
|
||||
// relation.
|
||||
bool IsEquivalent(const T& value1, const T& value2) const {
|
||||
return Find(&value1) == Find(&value2);
|
||||
}
|
||||
|
||||
// Returns all values known to be part of the equivalence relation.
|
||||
std::vector<const T*> GetAllKnownValues() const {
|
||||
std::vector<const T*> result;
|
||||
for (auto& value : owned_values_) {
|
||||
result.push_back(value.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if and only if |value| is known to be part of the equivalence
|
||||
// relation.
|
||||
bool Exists(const T& value) const {
|
||||
return value_set_.find(&value) != value_set_.end();
|
||||
}
|
||||
|
||||
// Returns the representative of the equivalence class of |value|, which must
|
||||
// already be known to the equivalence relation. This is the 'Find' operation
|
||||
// in a classic union-find data structure.
|
||||
const T* Find(const T* value) const {
|
||||
assert(Exists(*value));
|
||||
|
||||
// Get the canonical pointer to the value from the value pool.
|
||||
const T* known_value = *value_set_.find(value);
|
||||
assert(parent_[known_value] && "Every known value should have a parent.");
|
||||
|
||||
// Compute the result by chasing parents until we find a value that is its
|
||||
// own parent.
|
||||
const T* result = known_value;
|
||||
while (parent_[result] != result) {
|
||||
result = parent_[result];
|
||||
}
|
||||
assert(result && "Representatives should never be null.");
|
||||
|
||||
// At this point, |result| is the representative of the equivalence class.
|
||||
// Now perform the 'path compression' optimization by doing another pass up
|
||||
// the parent chain, setting the parent of each node to be the
|
||||
// representative, and rewriting children correspondingly.
|
||||
const T* current = known_value;
|
||||
while (parent_[current] != result) {
|
||||
const T* next = parent_[current];
|
||||
parent_[current] = result;
|
||||
children_[result].push_back(current);
|
||||
auto child_iterator =
|
||||
std::find(children_[next].begin(), children_[next].end(), current);
|
||||
assert(child_iterator != children_[next].end() &&
|
||||
"'next' is the parent of 'current', so 'current' should be a "
|
||||
"child of 'next'");
|
||||
children_[next].erase(child_iterator);
|
||||
current = next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
// Maps every value to a parent. The representative of an equivalence class
|
||||
// is its own parent. A value's representative can be found by walking its
|
||||
// chain of ancestors.
|
||||
//
|
||||
// Mutable because the intuitively const method, 'Find', performs path
|
||||
// compression.
|
||||
mutable std::unordered_map<const T*, const T*> parent_;
|
||||
|
||||
// Stores the children of each value. This allows the equivalence class of
|
||||
// a value to be calculated by traversing all descendents of the class's
|
||||
// representative.
|
||||
//
|
||||
// Mutable because the intuitively const method, 'Find', performs path
|
||||
// compression.
|
||||
mutable std::unordered_map<const T*, std::vector<const T*>> children_;
|
||||
|
||||
// The values known to the equivalence relation are allocated in
|
||||
// |owned_values_|, and |value_pool_| provides (via |PointerHashT| and
|
||||
// |PointerEqualsT|) a means for mapping a value of interest to a pointer
|
||||
// into an equivalent value in |owned_values_|.
|
||||
std::unordered_set<const T*, PointerHashT, PointerEqualsT> value_set_;
|
||||
std::vector<std::unique_ptr<T>> owned_values_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_EQUIVALENCE_RELATION_H_
|
||||
@@ -1,235 +0,0 @@
|
||||
// Copyright (c) 2019 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/fact_manager/constant_uniform_facts.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
uint32_t ConstantUniformFacts::GetConstantId(
|
||||
opt::IRContext* context,
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact,
|
||||
uint32_t type_id) {
|
||||
auto type = context->get_type_mgr()->GetType(type_id);
|
||||
assert(type != nullptr && "Unknown type id.");
|
||||
const opt::analysis::Constant* known_constant;
|
||||
if (type->AsInteger()) {
|
||||
opt::analysis::IntConstant candidate_constant(
|
||||
type->AsInteger(), GetConstantWords(constant_uniform_fact));
|
||||
known_constant =
|
||||
context->get_constant_mgr()->FindConstant(&candidate_constant);
|
||||
} else {
|
||||
assert(
|
||||
type->AsFloat() &&
|
||||
"Uniform constant facts are only supported for int and float types.");
|
||||
opt::analysis::FloatConstant candidate_constant(
|
||||
type->AsFloat(), GetConstantWords(constant_uniform_fact));
|
||||
known_constant =
|
||||
context->get_constant_mgr()->FindConstant(&candidate_constant);
|
||||
}
|
||||
if (!known_constant) {
|
||||
return 0;
|
||||
}
|
||||
return context->get_constant_mgr()->FindDeclaredConstant(known_constant,
|
||||
type_id);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ConstantUniformFacts::GetConstantWords(
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact) {
|
||||
std::vector<uint32_t> result;
|
||||
for (auto constant_word : constant_uniform_fact.constant_word()) {
|
||||
result.push_back(constant_word);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConstantUniformFacts::DataMatches(
|
||||
const opt::Instruction& constant_instruction,
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact) {
|
||||
assert(constant_instruction.opcode() == SpvOpConstant);
|
||||
std::vector<uint32_t> data_in_constant;
|
||||
for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) {
|
||||
data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i));
|
||||
}
|
||||
return data_in_constant == GetConstantWords(constant_uniform_fact);
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
ConstantUniformFacts::GetConstantsAvailableFromUniformsForType(
|
||||
opt::IRContext* ir_context, uint32_t type_id) const {
|
||||
std::vector<uint32_t> result;
|
||||
std::set<uint32_t> already_seen;
|
||||
for (auto& fact_and_type_id : facts_and_type_ids_) {
|
||||
if (fact_and_type_id.second != type_id) {
|
||||
continue;
|
||||
}
|
||||
if (auto constant_id =
|
||||
GetConstantId(ir_context, fact_and_type_id.first, type_id)) {
|
||||
if (already_seen.find(constant_id) == already_seen.end()) {
|
||||
result.push_back(constant_id);
|
||||
already_seen.insert(constant_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<protobufs::UniformBufferElementDescriptor>
|
||||
ConstantUniformFacts::GetUniformDescriptorsForConstant(
|
||||
opt::IRContext* ir_context, uint32_t constant_id) const {
|
||||
std::vector<protobufs::UniformBufferElementDescriptor> result;
|
||||
auto constant_inst = ir_context->get_def_use_mgr()->GetDef(constant_id);
|
||||
assert(constant_inst->opcode() == SpvOpConstant &&
|
||||
"The given id must be that of a constant");
|
||||
auto type_id = constant_inst->type_id();
|
||||
for (auto& fact_and_type_id : facts_and_type_ids_) {
|
||||
if (fact_and_type_id.second != type_id) {
|
||||
continue;
|
||||
}
|
||||
if (DataMatches(*constant_inst, fact_and_type_id.first)) {
|
||||
result.emplace_back(
|
||||
fact_and_type_id.first.uniform_buffer_element_descriptor());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ConstantUniformFacts::GetConstantFromUniformDescriptor(
|
||||
opt::IRContext* context,
|
||||
const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
|
||||
// Consider each fact.
|
||||
for (auto& fact_and_type : facts_and_type_ids_) {
|
||||
// Check whether the uniform descriptor associated with the fact matches
|
||||
// |uniform_descriptor|.
|
||||
if (UniformBufferElementDescriptorEquals()(
|
||||
&uniform_descriptor,
|
||||
&fact_and_type.first.uniform_buffer_element_descriptor())) {
|
||||
return GetConstantId(context, fact_and_type.first, fact_and_type.second);
|
||||
}
|
||||
}
|
||||
// No fact associated with the given uniform descriptor was found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown() const {
|
||||
std::vector<uint32_t> result;
|
||||
for (auto& fact_and_type : facts_and_type_ids_) {
|
||||
if (std::find(result.begin(), result.end(), fact_and_type.second) ==
|
||||
result.end()) {
|
||||
result.push_back(fact_and_type.second);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConstantUniformFacts::FloatingPointValueIsSuitable(
|
||||
const protobufs::FactConstantUniform& fact, uint32_t width) {
|
||||
const uint32_t kFloatWidth = 32;
|
||||
const uint32_t kDoubleWidth = 64;
|
||||
if (width != kFloatWidth && width != kDoubleWidth) {
|
||||
// Only 32- and 64-bit floating-point types are handled.
|
||||
return false;
|
||||
}
|
||||
std::vector<uint32_t> words = GetConstantWords(fact);
|
||||
if (width == 32) {
|
||||
float value;
|
||||
memcpy(&value, words.data(), sizeof(float));
|
||||
if (!std::isfinite(value)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
double value;
|
||||
memcpy(&value, words.data(), sizeof(double));
|
||||
if (!std::isfinite(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConstantUniformFacts::AddFact(const protobufs::FactConstantUniform& fact,
|
||||
opt::IRContext* context) {
|
||||
// Try to find a unique instruction that declares a variable such that the
|
||||
// variable is decorated with the descriptor set and binding associated with
|
||||
// the constant uniform fact.
|
||||
opt::Instruction* uniform_variable = FindUniformVariable(
|
||||
fact.uniform_buffer_element_descriptor(), context, true);
|
||||
|
||||
if (!uniform_variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(SpvOpVariable == uniform_variable->opcode());
|
||||
assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
|
||||
|
||||
auto should_be_uniform_pointer_type =
|
||||
context->get_type_mgr()->GetType(uniform_variable->type_id());
|
||||
if (!should_be_uniform_pointer_type->AsPointer()) {
|
||||
return false;
|
||||
}
|
||||
if (should_be_uniform_pointer_type->AsPointer()->storage_class() !=
|
||||
SpvStorageClassUniform) {
|
||||
return false;
|
||||
}
|
||||
auto should_be_uniform_pointer_instruction =
|
||||
context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
|
||||
auto composite_type =
|
||||
should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
|
||||
|
||||
auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, composite_type,
|
||||
fact.uniform_buffer_element_descriptor().index());
|
||||
if (!final_element_type_id) {
|
||||
return false;
|
||||
}
|
||||
auto final_element_type =
|
||||
context->get_type_mgr()->GetType(final_element_type_id);
|
||||
assert(final_element_type &&
|
||||
"There should be a type corresponding to this id.");
|
||||
|
||||
if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) {
|
||||
return false;
|
||||
}
|
||||
auto width = final_element_type->AsFloat()
|
||||
? final_element_type->AsFloat()->width()
|
||||
: final_element_type->AsInteger()->width();
|
||||
|
||||
if (final_element_type->AsFloat() &&
|
||||
!FloatingPointValueIsSuitable(fact, width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto required_words = (width + 32 - 1) / 32;
|
||||
if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
|
||||
return false;
|
||||
}
|
||||
facts_and_type_ids_.emplace_back(
|
||||
std::pair<protobufs::FactConstantUniform, uint32_t>(
|
||||
fact, final_element_type_id));
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
|
||||
ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
|
||||
return facts_and_type_ids_;
|
||||
}
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about uniform constants.
|
||||
class ConstantUniformFacts {
|
||||
public:
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool AddFact(const protobufs::FactConstantUniform& fact,
|
||||
opt::IRContext* context);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
|
||||
opt::IRContext* ir_context, uint32_t type_id) const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
std::vector<protobufs::UniformBufferElementDescriptor>
|
||||
GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
|
||||
uint32_t constant_id) const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
uint32_t GetConstantFromUniformDescriptor(
|
||||
opt::IRContext* context,
|
||||
const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
|
||||
const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
|
||||
GetConstantUniformFactsAndTypes() const;
|
||||
|
||||
private:
|
||||
// Returns true if and only if the words associated with
|
||||
// |constant_instruction| exactly match the words for the constant associated
|
||||
// with |constant_uniform_fact|.
|
||||
static bool DataMatches(
|
||||
const opt::Instruction& constant_instruction,
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact);
|
||||
|
||||
// Yields the constant words associated with |constant_uniform_fact|.
|
||||
static std::vector<uint32_t> GetConstantWords(
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact);
|
||||
|
||||
// Yields the id of a constant of type |type_id| whose data matches the
|
||||
// constant data in |constant_uniform_fact|, or 0 if no such constant is
|
||||
// declared.
|
||||
static uint32_t GetConstantId(
|
||||
opt::IRContext* context,
|
||||
const protobufs::FactConstantUniform& constant_uniform_fact,
|
||||
uint32_t type_id);
|
||||
|
||||
// Checks that the width of a floating-point constant is supported, and that
|
||||
// the constant is finite.
|
||||
static bool FloatingPointValueIsSuitable(
|
||||
const protobufs::FactConstantUniform& fact, uint32_t width);
|
||||
|
||||
std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
|
||||
facts_and_type_ids_;
|
||||
};
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_
|
||||
@@ -1,861 +0,0 @@
|
||||
// Copyright (c) 2019 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/fact_manager/data_synonym_and_id_equation_facts.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
size_t DataSynonymAndIdEquationFacts::OperationHash::operator()(
|
||||
const Operation& operation) const {
|
||||
std::u32string hash;
|
||||
hash.push_back(operation.opcode);
|
||||
for (auto operand : operation.operands) {
|
||||
hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
|
||||
}
|
||||
return std::hash<std::u32string>()(hash);
|
||||
}
|
||||
|
||||
bool DataSynonymAndIdEquationFacts::OperationEquals::operator()(
|
||||
const Operation& first, const Operation& second) const {
|
||||
// Equal operations require...
|
||||
//
|
||||
// Equal opcodes.
|
||||
if (first.opcode != second.opcode) {
|
||||
return false;
|
||||
}
|
||||
// Matching operand counts.
|
||||
if (first.operands.size() != second.operands.size()) {
|
||||
return false;
|
||||
}
|
||||
// Equal operands.
|
||||
for (uint32_t i = 0; i < first.operands.size(); i++) {
|
||||
if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::AddFact(
|
||||
const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550)
|
||||
// Assert that ids are not irrelevant.
|
||||
|
||||
// Add the fact, including all facts relating sub-components of the data
|
||||
// descriptors that are involved.
|
||||
AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::AddFact(
|
||||
const protobufs::FactIdEquation& fact, opt::IRContext* context) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550)
|
||||
// Assert that ids are not irrelevant.
|
||||
|
||||
protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
|
||||
|
||||
// Register the LHS in the equivalence relation if needed.
|
||||
RegisterDataDescriptor(lhs_dd);
|
||||
|
||||
// Get equivalence class representatives for all ids used on the RHS of the
|
||||
// equation.
|
||||
std::vector<const protobufs::DataDescriptor*> rhs_dds;
|
||||
for (auto rhs_id : fact.rhs_id()) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550)
|
||||
// Assert that ids are not irrelevant.
|
||||
|
||||
// Register a data descriptor based on this id in the equivalence relation
|
||||
// if needed, and then record the equivalence class representative.
|
||||
rhs_dds.push_back(RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {})));
|
||||
}
|
||||
|
||||
// Now add the fact.
|
||||
AddEquationFactRecursive(lhs_dd, static_cast<SpvOp>(fact.opcode()), rhs_dds,
|
||||
context);
|
||||
}
|
||||
|
||||
DataSynonymAndIdEquationFacts::OperationSet
|
||||
DataSynonymAndIdEquationFacts::GetEquations(
|
||||
const protobufs::DataDescriptor* lhs) const {
|
||||
auto existing = id_equations_.find(lhs);
|
||||
if (existing == id_equations_.end()) {
|
||||
return OperationSet();
|
||||
}
|
||||
return existing->second;
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
|
||||
const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
|
||||
opt::IRContext* context) {
|
||||
assert(synonymous_.Exists(lhs_dd) &&
|
||||
"The LHS must be known to the equivalence relation.");
|
||||
for (auto rhs_dd : rhs_dds) {
|
||||
// Keep release compilers happy.
|
||||
(void)(rhs_dd);
|
||||
assert(synonymous_.Exists(*rhs_dd) &&
|
||||
"The RHS operands must be known to the equivalence relation.");
|
||||
}
|
||||
|
||||
auto lhs_dd_representative = synonymous_.Find(&lhs_dd);
|
||||
|
||||
if (id_equations_.count(lhs_dd_representative) == 0) {
|
||||
// We have not seen an equation with this LHS before, so associate the LHS
|
||||
// with an initially empty set.
|
||||
id_equations_.insert({lhs_dd_representative, OperationSet()});
|
||||
}
|
||||
|
||||
{
|
||||
auto existing_equations = id_equations_.find(lhs_dd_representative);
|
||||
assert(existing_equations != id_equations_.end() &&
|
||||
"A set of operations should be present, even if empty.");
|
||||
|
||||
Operation new_operation = {opcode, rhs_dds};
|
||||
if (existing_equations->second.count(new_operation)) {
|
||||
// This equation is known, so there is nothing further to be done.
|
||||
return;
|
||||
}
|
||||
// Add the equation to the set of known equations.
|
||||
existing_equations->second.insert(new_operation);
|
||||
}
|
||||
|
||||
// Now try to work out corollaries implied by the new equation and existing
|
||||
// facts.
|
||||
switch (opcode) {
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
|
||||
break;
|
||||
case SpvOpBitcast: {
|
||||
assert(DataDescriptorsAreWellFormedAndComparable(context, lhs_dd,
|
||||
*rhs_dds[0]) &&
|
||||
"Operands of OpBitcast equation fact must have compatible types");
|
||||
if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) {
|
||||
AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0], context);
|
||||
}
|
||||
} break;
|
||||
case SpvOpIAdd: {
|
||||
// Equation form: "a = b + c"
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = (d - e) + c"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
|
||||
// Equation form: "a = (d - c) + c"
|
||||
// We can thus infer "a = d"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
|
||||
}
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
|
||||
// Equation form: "a = (c - e) + c"
|
||||
// We can thus infer "a = -e"
|
||||
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
|
||||
{equation.operands[1]}, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& equation : GetEquations(rhs_dds[1])) {
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = b + (d - e)"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
|
||||
// Equation form: "a = b + (d - b)"
|
||||
// We can thus infer "a = d"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpISub: {
|
||||
// Equation form: "a = b - c"
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == SpvOpIAdd) {
|
||||
// Equation form: "a = (d + e) - c"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
|
||||
// Equation form: "a = (c + e) - c"
|
||||
// We can thus infer "a = e"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
|
||||
}
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
|
||||
// Equation form: "a = (d + c) - c"
|
||||
// We can thus infer "a = d"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
|
||||
}
|
||||
}
|
||||
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = (d - e) - c"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
|
||||
// Equation form: "a = (c - e) - c"
|
||||
// We can thus infer "a = -e"
|
||||
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
|
||||
{equation.operands[1]}, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& equation : GetEquations(rhs_dds[1])) {
|
||||
if (equation.opcode == SpvOpIAdd) {
|
||||
// Equation form: "a = b - (d + e)"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
|
||||
// Equation form: "a = b - (b + e)"
|
||||
// We can thus infer "a = -e"
|
||||
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
|
||||
{equation.operands[1]}, context);
|
||||
}
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
|
||||
// Equation form: "a = b - (d + b)"
|
||||
// We can thus infer "a = -d"
|
||||
AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
|
||||
{equation.operands[0]}, context);
|
||||
}
|
||||
}
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = b - (d - e)"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
|
||||
// Equation form: "a = b - (b - e)"
|
||||
// We can thus infer "a = e"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1], context);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpLogicalNot:
|
||||
case SpvOpSNegate: {
|
||||
// Equation form: "a = !b" or "a = -b"
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == opcode) {
|
||||
// Equation form: "a = !!b" or "a = -(-b)"
|
||||
// We can thus infer "a = b"
|
||||
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
|
||||
const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context) {
|
||||
assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
|
||||
|
||||
// Record that the data descriptors provided in the fact are equivalent.
|
||||
MakeEquivalent(dd1, dd2);
|
||||
assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) &&
|
||||
"|dd1| and |dd2| must have a single representative");
|
||||
|
||||
// Compute various corollary facts.
|
||||
|
||||
// |dd1| and |dd2| belong to the same equivalence class so it doesn't matter
|
||||
// which one we use here.
|
||||
ComputeConversionDataSynonymFacts(dd1, context);
|
||||
|
||||
ComputeCompositeDataSynonymFacts(dd1, dd2, context);
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::ComputeConversionDataSynonymFacts(
|
||||
const protobufs::DataDescriptor& dd, opt::IRContext* context) {
|
||||
assert(synonymous_.Exists(dd) &&
|
||||
"|dd| should've been registered in the equivalence relation");
|
||||
|
||||
const auto* representative = synonymous_.Find(&dd);
|
||||
assert(representative &&
|
||||
"Representative can't be null for a registered descriptor");
|
||||
|
||||
const auto* type =
|
||||
context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, fuzzerutil::GetTypeId(context, representative->object()),
|
||||
representative->index()));
|
||||
assert(type && "Data descriptor has invalid type");
|
||||
|
||||
if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) ||
|
||||
type->AsInteger()) {
|
||||
// If there exist equation facts of the form |%a = opcode %representative|
|
||||
// and |%b = opcode %representative| where |opcode| is either OpConvertSToF
|
||||
// or OpConvertUToF, then |a| and |b| are synonymous.
|
||||
std::vector<const protobufs::DataDescriptor*> convert_s_to_f_lhs;
|
||||
std::vector<const protobufs::DataDescriptor*> convert_u_to_f_lhs;
|
||||
|
||||
for (const auto& fact : id_equations_) {
|
||||
for (const auto& equation : fact.second) {
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) {
|
||||
if (equation.opcode == SpvOpConvertSToF) {
|
||||
convert_s_to_f_lhs.push_back(fact.first);
|
||||
} else if (equation.opcode == SpvOpConvertUToF) {
|
||||
convert_u_to_f_lhs.push_back(fact.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& synonyms :
|
||||
{std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) {
|
||||
for (const auto* synonym_a : synonyms) {
|
||||
for (const auto* synonym_b : synonyms) {
|
||||
if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) &&
|
||||
DataDescriptorsAreWellFormedAndComparable(context, *synonym_a,
|
||||
*synonym_b)) {
|
||||
// |synonym_a| and |synonym_b| have compatible types - they are
|
||||
// synonymous.
|
||||
AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::ComputeCompositeDataSynonymFacts(
|
||||
const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context) {
|
||||
// Check whether this is a synonym about composite objects. If it is,
|
||||
// we can recursively add synonym facts about their associated sub-components.
|
||||
|
||||
// Get the type of the object referred to by the first data descriptor in the
|
||||
// synonym fact.
|
||||
uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
|
||||
dd1.index());
|
||||
auto type = context->get_type_mgr()->GetType(type_id);
|
||||
auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
|
||||
assert(type != nullptr &&
|
||||
"Invalid data synonym fact: one side has an unknown type.");
|
||||
|
||||
// Check whether the type is composite, recording the number of elements
|
||||
// associated with the composite if so.
|
||||
uint32_t num_composite_elements;
|
||||
if (type->AsArray()) {
|
||||
num_composite_elements =
|
||||
fuzzerutil::GetArraySize(*type_instruction, context);
|
||||
} else if (type->AsMatrix()) {
|
||||
num_composite_elements = type->AsMatrix()->element_count();
|
||||
} else if (type->AsStruct()) {
|
||||
num_composite_elements =
|
||||
fuzzerutil::GetNumberOfStructMembers(*type_instruction);
|
||||
} else if (type->AsVector()) {
|
||||
num_composite_elements = type->AsVector()->element_count();
|
||||
} else {
|
||||
// The type is not a composite, so return.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the fact has the form:
|
||||
// obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
|
||||
// then for each composite index i, we add a fact of the form:
|
||||
// obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
|
||||
//
|
||||
// However, to avoid adding a large number of synonym facts e.g. in the case
|
||||
// of arrays, we bound the number of composite elements to which this is
|
||||
// applied. Nevertheless, we always add a synonym fact for the final
|
||||
// components, as this may be an interesting edge case.
|
||||
|
||||
// The bound on the number of indices of the composite pair to note as being
|
||||
// synonymous.
|
||||
const uint32_t kCompositeElementBound = 10;
|
||||
|
||||
for (uint32_t i = 0; i < num_composite_elements;) {
|
||||
std::vector<uint32_t> extended_indices1 =
|
||||
fuzzerutil::RepeatedFieldToVector(dd1.index());
|
||||
extended_indices1.push_back(i);
|
||||
std::vector<uint32_t> extended_indices2 =
|
||||
fuzzerutil::RepeatedFieldToVector(dd2.index());
|
||||
extended_indices2.push_back(i);
|
||||
AddDataSynonymFactRecursive(
|
||||
MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
|
||||
MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
|
||||
context);
|
||||
|
||||
if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) {
|
||||
// We have not reached the bound yet, or have already skipped ahead to the
|
||||
// last element, so increment the loop counter as standard.
|
||||
i++;
|
||||
} else {
|
||||
// We have reached the bound, so skip ahead to the last element.
|
||||
assert(i == kCompositeElementBound - 1);
|
||||
i = num_composite_elements - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
|
||||
opt::IRContext* context, uint32_t maximum_equivalence_class_size) {
|
||||
// Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
|
||||
// data descriptors that describe objects of the same composite type, and that
|
||||
// the composite type is comprised of k components.
|
||||
//
|
||||
// For example, if m is a mat4x4 and v a vec4, we might consider:
|
||||
// m[2]: describes the 2nd column of m, a vec4
|
||||
// v[]: describes all of v, a vec4
|
||||
//
|
||||
// Suppose that we know, for every 0 <= i < k, that the fact:
|
||||
// obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
|
||||
// holds - i.e. that the children of the two data descriptors are synonymous.
|
||||
//
|
||||
// Then we can conclude that:
|
||||
// obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n]
|
||||
// holds.
|
||||
//
|
||||
// For instance, if we have the facts:
|
||||
// m[2, 0] == v[0]
|
||||
// m[2, 1] == v[1]
|
||||
// m[2, 2] == v[2]
|
||||
// m[2, 3] == v[3]
|
||||
// then we can conclude that:
|
||||
// m[2] == v.
|
||||
//
|
||||
// This method repeatedly searches the equivalence relation of data
|
||||
// descriptors, deducing and adding such facts, until a pass over the
|
||||
// relation leads to no further facts being deduced.
|
||||
|
||||
// The method relies on working with pairs of data descriptors, and in
|
||||
// particular being able to hash and compare such pairs.
|
||||
|
||||
using DataDescriptorPair =
|
||||
std::pair<protobufs::DataDescriptor, protobufs::DataDescriptor>;
|
||||
|
||||
struct DataDescriptorPairHash {
|
||||
std::size_t operator()(const DataDescriptorPair& pair) const {
|
||||
return DataDescriptorHash()(&pair.first) ^
|
||||
DataDescriptorHash()(&pair.second);
|
||||
}
|
||||
};
|
||||
|
||||
struct DataDescriptorPairEquals {
|
||||
bool operator()(const DataDescriptorPair& first,
|
||||
const DataDescriptorPair& second) const {
|
||||
return (DataDescriptorEquals()(&first.first, &second.first) &&
|
||||
DataDescriptorEquals()(&first.second, &second.second)) ||
|
||||
(DataDescriptorEquals()(&first.first, &second.second) &&
|
||||
DataDescriptorEquals()(&first.second, &second.first));
|
||||
}
|
||||
};
|
||||
|
||||
// This map records, for a given pair of composite data descriptors of the
|
||||
// same type, all the indices at which the data descriptors are known to be
|
||||
// synonymous. A pair is a key to this map only if we have observed that
|
||||
// the pair are synonymous at *some* index, but not at *all* indices.
|
||||
// Once we find that a pair of data descriptors are equivalent at all indices
|
||||
// we record the fact that they are synonymous and remove them from the map.
|
||||
//
|
||||
// Using the m and v example from above, initially the pair (m[2], v) would
|
||||
// not be a key to the map. If we find that m[2, 2] == v[2] holds, we would
|
||||
// add an entry:
|
||||
// (m[2], v) -> [false, false, true, false]
|
||||
// to record that they are synonymous at index 2. If we then find that
|
||||
// m[2, 0] == v[0] holds, we would update this entry to:
|
||||
// (m[2], v) -> [true, false, true, false]
|
||||
// If we then find that m[2, 3] == v[3] holds, we would update this entry to:
|
||||
// (m[2], v) -> [true, false, true, true]
|
||||
// Finally, if we then find that m[2, 1] == v[1] holds, which would make the
|
||||
// boolean vector true at every index, we would add the fact:
|
||||
// m[2] == v
|
||||
// to the equivalence relation and remove (m[2], v) from the map.
|
||||
std::unordered_map<DataDescriptorPair, std::vector<bool>,
|
||||
DataDescriptorPairHash, DataDescriptorPairEquals>
|
||||
candidate_composite_synonyms;
|
||||
|
||||
// We keep looking for new facts until we perform a complete pass over the
|
||||
// equivalence relation without finding any new facts.
|
||||
while (closure_computation_required_) {
|
||||
// We have not found any new facts yet during this pass; we set this to
|
||||
// 'true' if we do find a new fact.
|
||||
closure_computation_required_ = false;
|
||||
|
||||
// Consider each class in the equivalence relation.
|
||||
for (auto representative :
|
||||
synonymous_.GetEquivalenceClassRepresentatives()) {
|
||||
auto equivalence_class = synonymous_.GetEquivalenceClass(*representative);
|
||||
|
||||
if (equivalence_class.size() > maximum_equivalence_class_size) {
|
||||
// This equivalence class is larger than the maximum size we are willing
|
||||
// to consider, so we skip it. This potentially leads to missed fact
|
||||
// deductions, but avoids excessive runtime for closure computation.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Consider every data descriptor in the equivalence class.
|
||||
for (auto dd1_it = equivalence_class.begin();
|
||||
dd1_it != equivalence_class.end(); ++dd1_it) {
|
||||
// If this data descriptor has no indices then it does not have the form
|
||||
// obj_1[a_1, ..., a_m, i], so move on.
|
||||
auto dd1 = *dd1_it;
|
||||
if (dd1->index_size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Consider every other data descriptor later in the equivalence class
|
||||
// (due to symmetry, there is no need to compare with previous data
|
||||
// descriptors).
|
||||
auto dd2_it = dd1_it;
|
||||
for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) {
|
||||
auto dd2 = *dd2_it;
|
||||
// If this data descriptor has no indices then it does not have the
|
||||
// form obj_2[b_1, ..., b_n, i], so move on.
|
||||
if (dd2->index_size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point we know that:
|
||||
// - |dd1| has the form obj_1[a_1, ..., a_m, i]
|
||||
// - |dd2| has the form obj_2[b_1, ..., b_n, j]
|
||||
assert(dd1->index_size() > 0 && dd2->index_size() > 0 &&
|
||||
"Control should not reach here if either data descriptor has "
|
||||
"no indices.");
|
||||
|
||||
// We are only interested if i == j.
|
||||
if (dd1->index(dd1->index_size() - 1) !=
|
||||
dd2->index(dd2->index_size() - 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t common_final_index = dd1->index(dd1->index_size() - 1);
|
||||
|
||||
// Make data descriptors |dd1_prefix| and |dd2_prefix| for
|
||||
// obj_1[a_1, ..., a_m]
|
||||
// and
|
||||
// obj_2[b_1, ..., b_n]
|
||||
// These are the two data descriptors we might be getting closer to
|
||||
// deducing as being synonymous, due to knowing that they are
|
||||
// synonymous when extended by a particular index.
|
||||
protobufs::DataDescriptor dd1_prefix;
|
||||
dd1_prefix.set_object(dd1->object());
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(dd1->index_size() - 1);
|
||||
i++) {
|
||||
dd1_prefix.add_index(dd1->index(i));
|
||||
}
|
||||
protobufs::DataDescriptor dd2_prefix;
|
||||
dd2_prefix.set_object(dd2->object());
|
||||
for (uint32_t i = 0; i < static_cast<uint32_t>(dd2->index_size() - 1);
|
||||
i++) {
|
||||
dd2_prefix.add_index(dd2->index(i));
|
||||
}
|
||||
assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) &&
|
||||
"By construction these prefixes should be different.");
|
||||
|
||||
// If we already know that these prefixes are synonymous, move on.
|
||||
if (synonymous_.Exists(dd1_prefix) &&
|
||||
synonymous_.Exists(dd2_prefix) &&
|
||||
synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the type of obj_1
|
||||
auto dd1_root_type_id =
|
||||
context->get_def_use_mgr()->GetDef(dd1->object())->type_id();
|
||||
// Use this type, together with a_1, ..., a_m, to get the type of
|
||||
// obj_1[a_1, ..., a_m].
|
||||
auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, dd1_root_type_id, dd1_prefix.index());
|
||||
|
||||
// Similarly, get the type of obj_2 and use it to get the type of
|
||||
// obj_2[b_1, ..., b_n].
|
||||
auto dd2_root_type_id =
|
||||
context->get_def_use_mgr()->GetDef(dd2->object())->type_id();
|
||||
auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, dd2_root_type_id, dd2_prefix.index());
|
||||
|
||||
// If the types of dd1_prefix and dd2_prefix are not the same, they
|
||||
// cannot be synonymous.
|
||||
if (dd1_prefix_type != dd2_prefix_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point, we know we have synonymous data descriptors of the
|
||||
// form:
|
||||
// obj_1[a_1, ..., a_m, i]
|
||||
// obj_2[b_1, ..., b_n, i]
|
||||
// with the same last_index i, such that:
|
||||
// obj_1[a_1, ..., a_m]
|
||||
// and
|
||||
// obj_2[b_1, ..., b_n]
|
||||
// have the same type.
|
||||
|
||||
// Work out how many components there are in the (common) commposite
|
||||
// type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n].
|
||||
// This depends on whether the composite type is array, matrix, struct
|
||||
// or vector.
|
||||
uint32_t num_components_in_composite;
|
||||
auto composite_type =
|
||||
context->get_type_mgr()->GetType(dd1_prefix_type);
|
||||
auto composite_type_instruction =
|
||||
context->get_def_use_mgr()->GetDef(dd1_prefix_type);
|
||||
if (composite_type->AsArray()) {
|
||||
num_components_in_composite =
|
||||
fuzzerutil::GetArraySize(*composite_type_instruction, context);
|
||||
if (num_components_in_composite == 0) {
|
||||
// This indicates that the array has an unknown size, in which
|
||||
// case we cannot be sure we have matched all of its elements with
|
||||
// synonymous elements of another array.
|
||||
continue;
|
||||
}
|
||||
} else if (composite_type->AsMatrix()) {
|
||||
num_components_in_composite =
|
||||
composite_type->AsMatrix()->element_count();
|
||||
} else if (composite_type->AsStruct()) {
|
||||
num_components_in_composite = fuzzerutil::GetNumberOfStructMembers(
|
||||
*composite_type_instruction);
|
||||
} else {
|
||||
assert(composite_type->AsVector());
|
||||
num_components_in_composite =
|
||||
composite_type->AsVector()->element_count();
|
||||
}
|
||||
|
||||
// We are one step closer to being able to say that |dd1_prefix| and
|
||||
// |dd2_prefix| are synonymous.
|
||||
DataDescriptorPair candidate_composite_synonym(dd1_prefix,
|
||||
dd2_prefix);
|
||||
|
||||
// We look up what we already know about this pair.
|
||||
auto existing_entry =
|
||||
candidate_composite_synonyms.find(candidate_composite_synonym);
|
||||
|
||||
if (existing_entry == candidate_composite_synonyms.end()) {
|
||||
// If this is the first time we have seen the pair, we make a vector
|
||||
// of size |num_components_in_composite| that is 'true' at the
|
||||
// common final index associated with |dd1| and |dd2|, and 'false'
|
||||
// everywhere else, and register this vector as being associated
|
||||
// with the pair.
|
||||
std::vector<bool> entry;
|
||||
for (uint32_t i = 0; i < num_components_in_composite; i++) {
|
||||
entry.push_back(i == common_final_index);
|
||||
}
|
||||
candidate_composite_synonyms[candidate_composite_synonym] = entry;
|
||||
existing_entry =
|
||||
candidate_composite_synonyms.find(candidate_composite_synonym);
|
||||
} else {
|
||||
// We have seen this pair of data descriptors before, and we now
|
||||
// know that they are synonymous at one further index, so we
|
||||
// update the entry to record that.
|
||||
existing_entry->second[common_final_index] = true;
|
||||
}
|
||||
assert(existing_entry != candidate_composite_synonyms.end());
|
||||
|
||||
// Check whether |dd1_prefix| and |dd2_prefix| are now known to match
|
||||
// at every sub-component.
|
||||
bool all_components_match = true;
|
||||
for (uint32_t i = 0; i < num_components_in_composite; i++) {
|
||||
if (!existing_entry->second[i]) {
|
||||
all_components_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_components_match) {
|
||||
// The two prefixes match on all sub-components, so we know that
|
||||
// they are synonymous. We add this fact *non-recursively*, as we
|
||||
// have deduced that |dd1_prefix| and |dd2_prefix| are synonymous
|
||||
// by observing that all their sub-components are already
|
||||
// synonymous.
|
||||
assert(DataDescriptorsAreWellFormedAndComparable(
|
||||
context, dd1_prefix, dd2_prefix));
|
||||
MakeEquivalent(dd1_prefix, dd2_prefix);
|
||||
// Now that we know this pair of data descriptors are synonymous,
|
||||
// there is no point recording how close they are to being
|
||||
// synonymous.
|
||||
candidate_composite_synonyms.erase(candidate_composite_synonym);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataSynonymAndIdEquationFacts::MakeEquivalent(
|
||||
const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
// Register the data descriptors if they are not already known to the
|
||||
// equivalence relation.
|
||||
RegisterDataDescriptor(dd1);
|
||||
RegisterDataDescriptor(dd2);
|
||||
|
||||
if (synonymous_.IsEquivalent(dd1, dd2)) {
|
||||
// The data descriptors are already known to be equivalent, so there is
|
||||
// nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// We must make the data descriptors equivalent, and also make sure any
|
||||
// equation facts known about their representatives are merged.
|
||||
|
||||
// Record the original equivalence class representatives of the data
|
||||
// descriptors.
|
||||
auto dd1_original_representative = synonymous_.Find(&dd1);
|
||||
auto dd2_original_representative = synonymous_.Find(&dd2);
|
||||
|
||||
// Make the data descriptors equivalent.
|
||||
synonymous_.MakeEquivalent(dd1, dd2);
|
||||
// As we have updated the equivalence relation, we might be able to deduce
|
||||
// more facts by performing a closure computation, so we record that such a
|
||||
// computation is required.
|
||||
closure_computation_required_ = true;
|
||||
|
||||
// At this point, exactly one of |dd1_original_representative| and
|
||||
// |dd2_original_representative| will be the representative of the combined
|
||||
// equivalence class. We work out which one of them is still the class
|
||||
// representative and which one is no longer the class representative.
|
||||
|
||||
auto still_representative = synonymous_.Find(dd1_original_representative) ==
|
||||
dd1_original_representative
|
||||
? dd1_original_representative
|
||||
: dd2_original_representative;
|
||||
auto no_longer_representative =
|
||||
still_representative == dd1_original_representative
|
||||
? dd2_original_representative
|
||||
: dd1_original_representative;
|
||||
|
||||
assert(no_longer_representative != still_representative &&
|
||||
"The current and former representatives cannot be the same.");
|
||||
|
||||
// We now need to add all equations about |no_longer_representative| to the
|
||||
// set of equations known about |still_representative|.
|
||||
|
||||
// Get the equations associated with |no_longer_representative|.
|
||||
auto no_longer_representative_id_equations =
|
||||
id_equations_.find(no_longer_representative);
|
||||
if (no_longer_representative_id_equations != id_equations_.end()) {
|
||||
// There are some equations to transfer. There might not yet be any
|
||||
// equations about |still_representative|; create an empty set of equations
|
||||
// if this is the case.
|
||||
if (!id_equations_.count(still_representative)) {
|
||||
id_equations_.insert({still_representative, OperationSet()});
|
||||
}
|
||||
auto still_representative_id_equations =
|
||||
id_equations_.find(still_representative);
|
||||
assert(still_representative_id_equations != id_equations_.end() &&
|
||||
"At this point there must be a set of equations.");
|
||||
// Add all the equations known about |no_longer_representative| to the set
|
||||
// of equations known about |still_representative|.
|
||||
still_representative_id_equations->second.insert(
|
||||
no_longer_representative_id_equations->second.begin(),
|
||||
no_longer_representative_id_equations->second.end());
|
||||
}
|
||||
// Delete the no longer-relevant equations about |no_longer_representative|.
|
||||
id_equations_.erase(no_longer_representative);
|
||||
}
|
||||
|
||||
const protobufs::DataDescriptor*
|
||||
DataSynonymAndIdEquationFacts::RegisterDataDescriptor(
|
||||
const protobufs::DataDescriptor& dd) {
|
||||
return synonymous_.Exists(dd) ? synonymous_.Find(&dd)
|
||||
: synonymous_.Register(dd);
|
||||
}
|
||||
|
||||
bool DataSynonymAndIdEquationFacts::DataDescriptorsAreWellFormedAndComparable(
|
||||
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
|
||||
dd1.index());
|
||||
auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
|
||||
dd2.index());
|
||||
// The end types of the data descriptors must exist.
|
||||
if (end_type_id_1 == 0 || end_type_id_2 == 0) {
|
||||
return false;
|
||||
}
|
||||
// Neither end type is allowed to be void.
|
||||
if (context->get_def_use_mgr()->GetDef(end_type_id_1)->opcode() ==
|
||||
SpvOpTypeVoid ||
|
||||
context->get_def_use_mgr()->GetDef(end_type_id_2)->opcode() ==
|
||||
SpvOpTypeVoid) {
|
||||
return false;
|
||||
}
|
||||
// If the end types are the same, the data descriptors are comparable.
|
||||
if (end_type_id_1 == end_type_id_2) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise they are only comparable if they are integer scalars or integer
|
||||
// vectors that differ only in signedness.
|
||||
|
||||
// Get both types.
|
||||
const auto* type_a = context->get_type_mgr()->GetType(end_type_id_1);
|
||||
const auto* type_b = context->get_type_mgr()->GetType(end_type_id_2);
|
||||
assert(type_a && type_b && "Data descriptors have invalid type(s)");
|
||||
|
||||
// If both types are numerical or vectors of numerical components, then they
|
||||
// are compatible if they have the same number of components and the same bit
|
||||
// count per component.
|
||||
|
||||
if (type_a->AsVector() && type_b->AsVector()) {
|
||||
const auto* vector_a = type_a->AsVector();
|
||||
const auto* vector_b = type_b->AsVector();
|
||||
|
||||
if (vector_a->element_count() != vector_b->element_count() ||
|
||||
vector_a->element_type()->AsBool() ||
|
||||
vector_b->element_type()->AsBool()) {
|
||||
// The case where both vectors have boolean elements and the same number
|
||||
// of components is handled by the direct equality check earlier.
|
||||
// You can't have multiple identical boolean vector types.
|
||||
return false;
|
||||
}
|
||||
|
||||
type_a = vector_a->element_type();
|
||||
type_b = vector_b->element_type();
|
||||
}
|
||||
|
||||
auto get_bit_count_for_numeric_type =
|
||||
[](const opt::analysis::Type& type) -> uint32_t {
|
||||
if (const auto* integer = type.AsInteger()) {
|
||||
return integer->width();
|
||||
} else if (const auto* floating = type.AsFloat()) {
|
||||
return floating->width();
|
||||
} else {
|
||||
assert(false && "|type| must be a numerical type");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Checks that both |type_a| and |type_b| are either numerical or vectors of
|
||||
// numerical components and have the same number of bits.
|
||||
return (type_a->AsInteger() || type_a->AsFloat()) &&
|
||||
(type_b->AsInteger() || type_b->AsFloat()) &&
|
||||
(get_bit_count_for_numeric_type(*type_a) ==
|
||||
get_bit_count_for_numeric_type(*type_b));
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*>
|
||||
DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
|
||||
const protobufs::DataDescriptor& data_descriptor) const {
|
||||
if (synonymous_.Exists(data_descriptor)) {
|
||||
return synonymous_.GetEquivalenceClass(data_descriptor);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown() const {
|
||||
std::vector<uint32_t> result;
|
||||
for (auto& data_descriptor : synonymous_.GetAllKnownValues()) {
|
||||
if (data_descriptor->index().empty()) {
|
||||
result.push_back(data_descriptor->object());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DataSynonymAndIdEquationFacts::IsSynonymous(
|
||||
const protobufs::DataDescriptor& data_descriptor1,
|
||||
const protobufs::DataDescriptor& data_descriptor2) const {
|
||||
return synonymous_.Exists(data_descriptor1) &&
|
||||
synonymous_.Exists(data_descriptor2) &&
|
||||
synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
|
||||
}
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,156 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/equivalence_relation.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about data synonyms and id equations.
|
||||
class DataSynonymAndIdEquationFacts {
|
||||
public:
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
|
||||
const protobufs::DataDescriptor& data_descriptor) const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
|
||||
const protobufs::DataDescriptor& data_descriptor2) const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
void ComputeClosureOfFacts(opt::IRContext* context,
|
||||
uint32_t maximum_equivalence_class_size);
|
||||
|
||||
private:
|
||||
// This helper struct represents the right hand side of an equation as an
|
||||
// operator applied to a number of data descriptor operands.
|
||||
struct Operation {
|
||||
SpvOp opcode;
|
||||
std::vector<const protobufs::DataDescriptor*> operands;
|
||||
};
|
||||
|
||||
// Hashing for operations, to allow deterministic unordered sets.
|
||||
struct OperationHash {
|
||||
size_t operator()(const Operation& operation) const;
|
||||
};
|
||||
|
||||
// Equality for operations, to allow deterministic unordered sets.
|
||||
struct OperationEquals {
|
||||
bool operator()(const Operation& first, const Operation& second) const;
|
||||
};
|
||||
|
||||
using OperationSet =
|
||||
std::unordered_set<Operation, OperationHash, OperationEquals>;
|
||||
|
||||
// Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
|
||||
// into sub-components of the data descriptors, if they are composites, to
|
||||
// record that their components are pairwise-synonymous.
|
||||
void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Computes various corollary facts from the data descriptor |dd| if members
|
||||
// of its equivalence class participate in equation facts with OpConvert*
|
||||
// opcodes. The descriptor should be registered in the equivalence relation.
|
||||
void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Recurses into sub-components of the data descriptors, if they are
|
||||
// composites, to record that their components are pairwise-synonymous.
|
||||
void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
|
||||
// of equations that are known about them.
|
||||
void MakeEquivalent(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
|
||||
// Registers a data descriptor in the equivalence relation if it hasn't been
|
||||
// registered yet, and returns its representative.
|
||||
const protobufs::DataDescriptor* RegisterDataDescriptor(
|
||||
const protobufs::DataDescriptor& dd);
|
||||
|
||||
// Returns true if and only if |dd1| and |dd2| are valid data descriptors
|
||||
// whose associated data have compatible types. Two types are compatible if:
|
||||
// - they are the same
|
||||
// - they both are numerical or vectors of numerical components with the same
|
||||
// number of components and the same bit count per component
|
||||
static bool DataDescriptorsAreWellFormedAndComparable(
|
||||
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
|
||||
OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
|
||||
|
||||
// Requires that |lhs_dd| and every element of |rhs_dds| is present in the
|
||||
// |synonymous_| equivalence relation, but is not necessarily its own
|
||||
// representative. Records the fact that the equation
|
||||
// "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any
|
||||
// corollaries, in the form of data synonym or equation facts, that follow
|
||||
// from this and other known facts.
|
||||
void AddEquationFactRecursive(
|
||||
const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
|
||||
const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
|
||||
opt::IRContext* context);
|
||||
|
||||
// The data descriptors that are known to be synonymous with one another are
|
||||
// captured by this equivalence relation.
|
||||
EquivalenceRelation<protobufs::DataDescriptor, DataDescriptorHash,
|
||||
DataDescriptorEquals>
|
||||
synonymous_;
|
||||
|
||||
// When a new synonym fact is added, it may be possible to deduce further
|
||||
// synonym facts by computing a closure of all known facts. However, this is
|
||||
// an expensive operation, so it should be performed sparingly and only there
|
||||
// is some chance of new facts being deduced. This boolean tracks whether a
|
||||
// closure computation is required - i.e., whether a new fact has been added
|
||||
// since the last time such a computation was performed.
|
||||
bool closure_computation_required_ = false;
|
||||
|
||||
// Represents a set of equations on data descriptors as a map indexed by
|
||||
// left-hand-side, mapping a left-hand-side to a set of operations, each of
|
||||
// which (together with the left-hand-side) defines an equation.
|
||||
//
|
||||
// All data descriptors occurring in equations are required to be present in
|
||||
// the |synonymous_| equivalence relation, and to be their own representatives
|
||||
// in that relation.
|
||||
std::unordered_map<const protobufs::DataDescriptor*, OperationSet>
|
||||
id_equations_;
|
||||
};
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2019 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/fact_manager/dead_block_facts.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
void DeadBlockFacts::AddFact(const protobufs::FactBlockIsDead& fact) {
|
||||
dead_block_ids_.insert(fact.block_id());
|
||||
}
|
||||
|
||||
bool DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
|
||||
return dead_block_ids_.count(block_id) != 0;
|
||||
}
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_DEAD_BLOCK_FACTS_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about data blocks.
|
||||
class DeadBlockFacts {
|
||||
public:
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactBlockIsDead& fact);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool BlockIsDead(uint32_t block_id) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<uint32_t> dead_block_ids_;
|
||||
};
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_
|
||||
@@ -1,244 +0,0 @@
|
||||
// Copyright (c) 2019 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 "fact_manager.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
std::string ToString(const protobufs::FactConstantUniform& fact) {
|
||||
std::stringstream stream;
|
||||
stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
|
||||
<< ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
|
||||
|
||||
bool first = true;
|
||||
for (auto index : fact.uniform_buffer_element_descriptor().index()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream << ", ";
|
||||
}
|
||||
stream << index;
|
||||
}
|
||||
|
||||
stream << "] == [";
|
||||
|
||||
first = true;
|
||||
for (auto constant_word : fact.constant_word()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream << ", ";
|
||||
}
|
||||
stream << constant_word;
|
||||
}
|
||||
|
||||
stream << "]";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string ToString(const protobufs::FactDataSynonym& fact) {
|
||||
std::stringstream stream;
|
||||
stream << fact.data1() << " = " << fact.data2();
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string ToString(const protobufs::FactIdEquation& fact) {
|
||||
std::stringstream stream;
|
||||
stream << fact.lhs_id();
|
||||
stream << " " << static_cast<SpvOp>(fact.opcode());
|
||||
for (auto rhs_id : fact.rhs_id()) {
|
||||
stream << " " << rhs_id;
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string ToString(const protobufs::Fact& fact) {
|
||||
switch (fact.fact_case()) {
|
||||
case protobufs::Fact::kConstantUniformFact:
|
||||
return ToString(fact.constant_uniform_fact());
|
||||
case protobufs::Fact::kDataSynonymFact:
|
||||
return ToString(fact.data_synonym_fact());
|
||||
case protobufs::Fact::kIdEquationFact:
|
||||
return ToString(fact.id_equation_fact());
|
||||
default:
|
||||
assert(false && "Stringification not supported for this fact.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void FactManager::AddFacts(const MessageConsumer& message_consumer,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
opt::IRContext* context) {
|
||||
for (auto& fact : initial_facts.fact()) {
|
||||
if (!AddFact(fact, context)) {
|
||||
auto message = "Invalid fact " + ToString(fact) + " ignored.";
|
||||
message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
|
||||
opt::IRContext* context) {
|
||||
switch (fact.fact_case()) {
|
||||
case protobufs::Fact::kConstantUniformFact:
|
||||
return constant_uniform_facts_.AddFact(fact.constant_uniform_fact(),
|
||||
context);
|
||||
case protobufs::Fact::kDataSynonymFact:
|
||||
data_synonym_and_id_equation_facts_.AddFact(fact.data_synonym_fact(),
|
||||
context);
|
||||
return true;
|
||||
case protobufs::Fact::kBlockIsDeadFact:
|
||||
dead_block_facts_.AddFact(fact.block_is_dead_fact());
|
||||
return true;
|
||||
case protobufs::Fact::kFunctionIsLivesafeFact:
|
||||
livesafe_function_facts_.AddFact(fact.function_is_livesafe_fact());
|
||||
return true;
|
||||
default:
|
||||
assert(false && "Unknown fact type.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
|
||||
const protobufs::DataDescriptor& data2,
|
||||
opt::IRContext* context) {
|
||||
protobufs::FactDataSynonym fact;
|
||||
*fact.mutable_data1() = data1;
|
||||
*fact.mutable_data2() = data2;
|
||||
data_synonym_and_id_equation_facts_.AddFact(fact, context);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
|
||||
opt::IRContext* ir_context, uint32_t type_id) const {
|
||||
return constant_uniform_facts_.GetConstantsAvailableFromUniformsForType(
|
||||
ir_context, type_id);
|
||||
}
|
||||
|
||||
std::vector<protobufs::UniformBufferElementDescriptor>
|
||||
FactManager::GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
|
||||
uint32_t constant_id) const {
|
||||
return constant_uniform_facts_.GetUniformDescriptorsForConstant(ir_context,
|
||||
constant_id);
|
||||
}
|
||||
|
||||
uint32_t FactManager::GetConstantFromUniformDescriptor(
|
||||
opt::IRContext* context,
|
||||
const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const {
|
||||
return constant_uniform_facts_.GetConstantFromUniformDescriptor(
|
||||
context, uniform_descriptor);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FactManager::GetTypesForWhichUniformValuesAreKnown()
|
||||
const {
|
||||
return constant_uniform_facts_.GetTypesForWhichUniformValuesAreKnown();
|
||||
}
|
||||
|
||||
const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
|
||||
FactManager::GetConstantUniformFactsAndTypes() const {
|
||||
return constant_uniform_facts_.GetConstantUniformFactsAndTypes();
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown() const {
|
||||
return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown();
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*>
|
||||
FactManager::GetSynonymsForDataDescriptor(
|
||||
const protobufs::DataDescriptor& data_descriptor) const {
|
||||
return data_synonym_and_id_equation_facts_.GetSynonymsForDataDescriptor(
|
||||
data_descriptor);
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
|
||||
uint32_t id) const {
|
||||
return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {}));
|
||||
}
|
||||
|
||||
bool FactManager::IsSynonymous(
|
||||
const protobufs::DataDescriptor& data_descriptor1,
|
||||
const protobufs::DataDescriptor& data_descriptor2) const {
|
||||
return data_synonym_and_id_equation_facts_.IsSynonymous(data_descriptor1,
|
||||
data_descriptor2);
|
||||
}
|
||||
|
||||
bool FactManager::BlockIsDead(uint32_t block_id) const {
|
||||
return dead_block_facts_.BlockIsDead(block_id);
|
||||
}
|
||||
|
||||
void FactManager::AddFactBlockIsDead(uint32_t block_id) {
|
||||
protobufs::FactBlockIsDead fact;
|
||||
fact.set_block_id(block_id);
|
||||
dead_block_facts_.AddFact(fact);
|
||||
}
|
||||
|
||||
bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
|
||||
return livesafe_function_facts_.FunctionIsLivesafe(function_id);
|
||||
}
|
||||
|
||||
void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
|
||||
protobufs::FactFunctionIsLivesafe fact;
|
||||
fact.set_function_id(function_id);
|
||||
livesafe_function_facts_.AddFact(fact);
|
||||
}
|
||||
|
||||
bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
|
||||
return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id);
|
||||
}
|
||||
|
||||
bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
|
||||
return irrelevant_value_facts_.IdIsIrrelevant(result_id);
|
||||
}
|
||||
|
||||
void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
|
||||
protobufs::FactPointeeValueIsIrrelevant fact;
|
||||
fact.set_pointer_id(pointer_id);
|
||||
irrelevant_value_facts_.AddFact(fact);
|
||||
}
|
||||
|
||||
void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
|
||||
protobufs::FactIdIsIrrelevant fact;
|
||||
fact.set_result_id(result_id);
|
||||
irrelevant_value_facts_.AddFact(fact);
|
||||
}
|
||||
|
||||
void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
|
||||
const std::vector<uint32_t>& rhs_id,
|
||||
opt::IRContext* context) {
|
||||
protobufs::FactIdEquation fact;
|
||||
fact.set_lhs_id(lhs_id);
|
||||
fact.set_opcode(opcode);
|
||||
for (auto an_rhs_id : rhs_id) {
|
||||
fact.add_rhs_id(an_rhs_id);
|
||||
}
|
||||
data_synonym_and_id_equation_facts_.AddFact(fact, context);
|
||||
}
|
||||
|
||||
void FactManager::ComputeClosureOfFacts(
|
||||
opt::IRContext* ir_context, uint32_t maximum_equivalence_class_size) {
|
||||
data_synonym_and_id_equation_facts_.ComputeClosureOfFacts(
|
||||
ir_context, maximum_equivalence_class_size);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,213 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_FACT_MANAGER_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/fact_manager/constant_uniform_facts.h"
|
||||
#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h"
|
||||
#include "source/fuzz/fact_manager/dead_block_facts.h"
|
||||
#include "source/fuzz/fact_manager/irrelevant_value_facts.h"
|
||||
#include "source/fuzz/fact_manager/livesafe_function_facts.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/constants.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Keeps track of facts about the module being transformed on which the fuzzing
|
||||
// process can depend. Some initial facts can be provided, for example about
|
||||
// guarantees on the values of inputs to SPIR-V entry points. Transformations
|
||||
// may then rely on these facts, can add further facts that they establish.
|
||||
// Facts are intended to be simple properties that either cannot be deduced from
|
||||
// the module (such as properties that are guaranteed to hold for entry point
|
||||
// inputs), or that are established by transformations, likely to be useful for
|
||||
// future transformations, and not completely trivial to deduce straight from
|
||||
// the module.
|
||||
class FactManager {
|
||||
public:
|
||||
// Adds all the facts from |facts|, checking them for validity with respect to
|
||||
// |context|. Warnings about invalid facts are communicated via
|
||||
// |message_consumer|; such facts are otherwise ignored.
|
||||
void AddFacts(const MessageConsumer& message_consumer,
|
||||
const protobufs::FactSequence& facts, opt::IRContext* context);
|
||||
|
||||
// Checks the fact for validity with respect to |context|. Returns false,
|
||||
// with no side effects, if the fact is invalid. Otherwise adds |fact| to the
|
||||
// fact manager.
|
||||
bool AddFact(const protobufs::Fact& fact, opt::IRContext* context);
|
||||
|
||||
// Record the fact that |data1| and |data2| are synonymous.
|
||||
void AddFactDataSynonym(const protobufs::DataDescriptor& data1,
|
||||
const protobufs::DataDescriptor& data2,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Records the fact that |block_id| is dead.
|
||||
void AddFactBlockIsDead(uint32_t block_id);
|
||||
|
||||
// Records the fact that |function_id| is livesafe.
|
||||
void AddFactFunctionIsLivesafe(uint32_t function_id);
|
||||
|
||||
// Records the fact that the value of the pointee associated with |pointer_id|
|
||||
// is irrelevant: it does not affect the observable behaviour of the module.
|
||||
void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
|
||||
|
||||
// Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
|
||||
// the semantics of the module)
|
||||
void AddFactIdIsIrrelevant(uint32_t result_id);
|
||||
|
||||
// Records the fact that |lhs_id| is defined by the equation:
|
||||
//
|
||||
// |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
|
||||
//
|
||||
void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
|
||||
const std::vector<uint32_t>& rhs_id,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Inspects all known facts and adds corollary facts; e.g. if we know that
|
||||
// a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
|
||||
// that a == b holds.
|
||||
//
|
||||
// This method is expensive, and should only be called (by applying a
|
||||
// transformation) at the start of a fuzzer pass that depends on data
|
||||
// synonym facts, rather than calling it every time a new data synonym fact
|
||||
// is added.
|
||||
//
|
||||
// The parameter |maximum_equivalence_class_size| specifies the size beyond
|
||||
// which equivalence classes should not be mined for new facts, to avoid
|
||||
// excessively-long closure computations.
|
||||
void ComputeClosureOfFacts(opt::IRContext* ir_context,
|
||||
uint32_t maximum_equivalence_class_size);
|
||||
|
||||
// The fact manager is responsible for managing a few distinct categories of
|
||||
// facts. In principle there could be different fact managers for each kind
|
||||
// of fact, but in practice providing one 'go to' place for facts is
|
||||
// convenient. To keep some separation, the public methods of the fact
|
||||
// manager should be grouped according to the kind of fact to which they
|
||||
// relate.
|
||||
|
||||
//==============================
|
||||
// Querying facts about uniform constants
|
||||
|
||||
// Provides the distinct type ids for which at least one "constant ==
|
||||
// uniform element" fact is known.
|
||||
std::vector<uint32_t> GetTypesForWhichUniformValuesAreKnown() const;
|
||||
|
||||
// Provides distinct constant ids with type |type_id| for which at least one
|
||||
// "constant == uniform element" fact is known. If multiple identically-
|
||||
// valued constants are relevant, only one will appear in the sequence.
|
||||
std::vector<uint32_t> GetConstantsAvailableFromUniformsForType(
|
||||
opt::IRContext* ir_context, uint32_t type_id) const;
|
||||
|
||||
// Provides details of all uniform elements that are known to be equal to the
|
||||
// constant associated with |constant_id| in |ir_context|.
|
||||
std::vector<protobufs::UniformBufferElementDescriptor>
|
||||
GetUniformDescriptorsForConstant(opt::IRContext* ir_context,
|
||||
uint32_t constant_id) const;
|
||||
|
||||
// Returns the id of a constant whose value is known to match that of
|
||||
// |uniform_descriptor|, and whose type matches the type of the uniform
|
||||
// element. If multiple such constant is exist, the one that is returned
|
||||
// is arbitrary. Returns 0 if no such constant id exists.
|
||||
uint32_t GetConstantFromUniformDescriptor(
|
||||
opt::IRContext* context,
|
||||
const protobufs::UniformBufferElementDescriptor& uniform_descriptor)
|
||||
const;
|
||||
|
||||
// Returns all "constant == uniform element" facts known to the fact
|
||||
// manager, pairing each fact with id of the type that is associated with
|
||||
// both the constant and the uniform element.
|
||||
const std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>&
|
||||
GetConstantUniformFactsAndTypes() const;
|
||||
|
||||
// End of uniform constant facts
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Querying facts about id synonyms
|
||||
|
||||
// Returns every id for which a fact of the form "this id is synonymous with
|
||||
// this piece of data" is known.
|
||||
std::vector<uint32_t> GetIdsForWhichSynonymsAreKnown() const;
|
||||
|
||||
// Returns the equivalence class of all known synonyms of |id|, or an empty
|
||||
// set if no synonyms are known.
|
||||
std::vector<const protobufs::DataDescriptor*> GetSynonymsForId(
|
||||
uint32_t id) const;
|
||||
|
||||
// Returns the equivalence class of all known synonyms of |data_descriptor|,
|
||||
// or empty if no synonyms are known.
|
||||
std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
|
||||
const protobufs::DataDescriptor& data_descriptor) const;
|
||||
|
||||
// Returns true if and ony if |data_descriptor1| and |data_descriptor2| are
|
||||
// known to be synonymous.
|
||||
bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1,
|
||||
const protobufs::DataDescriptor& data_descriptor2) const;
|
||||
|
||||
// End of id synonym facts
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Querying facts about dead blocks
|
||||
|
||||
// Returns true if and ony if |block_id| is the id of a block known to be
|
||||
// dynamically unreachable.
|
||||
bool BlockIsDead(uint32_t block_id) const;
|
||||
|
||||
// End of dead block facts
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Querying facts about livesafe function
|
||||
|
||||
// Returns true if and ony if |function_id| is the id of a function known
|
||||
// to be livesafe.
|
||||
bool FunctionIsLivesafe(uint32_t function_id) const;
|
||||
|
||||
// End of dead livesafe function facts
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Querying facts about irrelevant values
|
||||
|
||||
// Returns true if and ony if the value of the pointee associated with
|
||||
// |pointer_id| is irrelevant.
|
||||
bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
|
||||
|
||||
// Returns true iff there exists a fact that the |result_id| is irrelevant.
|
||||
bool IdIsIrrelevant(uint32_t result_id) const;
|
||||
|
||||
// End of irrelevant value facts
|
||||
//==============================
|
||||
|
||||
private:
|
||||
// Keep these in alphabetical order.
|
||||
fact_manager::ConstantUniformFacts constant_uniform_facts_;
|
||||
fact_manager::DataSynonymAndIdEquationFacts
|
||||
data_synonym_and_id_equation_facts_;
|
||||
fact_manager::DeadBlockFacts dead_block_facts_;
|
||||
fact_manager::LivesafeFunctionFacts livesafe_function_facts_;
|
||||
fact_manager::IrrelevantValueFacts irrelevant_value_facts_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2019 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/fact_manager/irrelevant_value_facts.h"
|
||||
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
void IrrelevantValueFacts::AddFact(
|
||||
const protobufs::FactPointeeValueIsIrrelevant& fact) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550)
|
||||
// Assert that the id does not participate in DataSynonym facts and is a
|
||||
// pointer.
|
||||
|
||||
pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
|
||||
}
|
||||
|
||||
void IrrelevantValueFacts::AddFact(const protobufs::FactIdIsIrrelevant& fact) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550)
|
||||
// Assert that the id does not participate in DataSynonym facts and is not a
|
||||
// pointer.
|
||||
|
||||
irrelevant_ids_.insert(fact.result_id());
|
||||
}
|
||||
|
||||
bool IrrelevantValueFacts::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
|
||||
return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
|
||||
}
|
||||
|
||||
bool IrrelevantValueFacts::IdIsIrrelevant(uint32_t pointer_id) const {
|
||||
return irrelevant_ids_.count(pointer_id) != 0;
|
||||
}
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about various irrelevant values in the module.
|
||||
class IrrelevantValueFacts {
|
||||
public:
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactIdIsIrrelevant& fact);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool IdIsIrrelevant(uint32_t pointer_id) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
|
||||
std::unordered_set<uint32_t> irrelevant_ids_;
|
||||
};
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) 2019 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/fact_manager/livesafe_function_facts.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
void LivesafeFunctionFacts::AddFact(
|
||||
const protobufs::FactFunctionIsLivesafe& fact) {
|
||||
livesafe_function_ids_.insert(fact.function_id());
|
||||
}
|
||||
|
||||
bool LivesafeFunctionFacts::FunctionIsLivesafe(uint32_t function_id) const {
|
||||
return livesafe_function_ids_.count(function_id) != 0;
|
||||
}
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) 2019 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_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_
|
||||
#define SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace fact_manager {
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about livesafe functions.
|
||||
class LivesafeFunctionFacts {
|
||||
public:
|
||||
// See method in FactManager which delegates to this method.
|
||||
void AddFact(const protobufs::FactFunctionIsLivesafe& fact);
|
||||
|
||||
// See method in FactManager which delegates to this method.
|
||||
bool FunctionIsLivesafe(uint32_t function_id) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<uint32_t> livesafe_function_ids_;
|
||||
};
|
||||
|
||||
} // namespace fact_manager
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_
|
||||
@@ -1,372 +0,0 @@
|
||||
// Copyright (c) 2019 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/force_render_red.h"
|
||||
|
||||
#include "source/fuzz/fact_manager/fact_manager.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/types.h"
|
||||
#include "source/util/make_unique.h"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper method to find the fragment shader entry point, complaining if there
|
||||
// is no shader or if there is no fragment entry point.
|
||||
opt::Function* FindFragmentShaderEntryPoint(opt::IRContext* ir_context,
|
||||
MessageConsumer message_consumer) {
|
||||
// Check that this is a fragment shader
|
||||
bool found_capability_shader = false;
|
||||
for (auto& capability : ir_context->capabilities()) {
|
||||
assert(capability.opcode() == SpvOpCapability);
|
||||
if (capability.GetSingleWordInOperand(0) == SpvCapabilityShader) {
|
||||
found_capability_shader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_capability_shader) {
|
||||
message_consumer(
|
||||
SPV_MSG_ERROR, nullptr, {},
|
||||
"Forcing of red rendering requires the Shader capability.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
opt::Instruction* fragment_entry_point = nullptr;
|
||||
for (auto& entry_point : ir_context->module()->entry_points()) {
|
||||
if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelFragment) {
|
||||
fragment_entry_point = &entry_point;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fragment_entry_point == nullptr) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Forcing of red rendering requires an entry point with "
|
||||
"the Fragment execution model.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& function : *ir_context->module()) {
|
||||
if (function.result_id() ==
|
||||
fragment_entry_point->GetSingleWordInOperand(1)) {
|
||||
return &function;
|
||||
}
|
||||
}
|
||||
assert(
|
||||
false &&
|
||||
"A valid module must have a function associate with each entry point.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Helper method to check that there is a single vec4 output variable and get a
|
||||
// pointer to it.
|
||||
opt::Instruction* FindVec4OutputVariable(opt::IRContext* ir_context,
|
||||
MessageConsumer message_consumer) {
|
||||
opt::Instruction* output_variable = nullptr;
|
||||
for (auto& inst : ir_context->types_values()) {
|
||||
if (inst.opcode() == SpvOpVariable &&
|
||||
inst.GetSingleWordInOperand(0) == SpvStorageClassOutput) {
|
||||
if (output_variable != nullptr) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Only one output variable can be handled at present; "
|
||||
"found multiple.");
|
||||
return nullptr;
|
||||
}
|
||||
output_variable = &inst;
|
||||
// Do not break, as we want to check for multiple output variables.
|
||||
}
|
||||
}
|
||||
if (output_variable == nullptr) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"No output variable to which to write red was found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto output_variable_base_type = ir_context->get_type_mgr()
|
||||
->GetType(output_variable->type_id())
|
||||
->AsPointer()
|
||||
->pointee_type()
|
||||
->AsVector();
|
||||
if (!output_variable_base_type ||
|
||||
output_variable_base_type->element_count() != 4 ||
|
||||
!output_variable_base_type->element_type()->AsFloat()) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"The output variable must have type vec4.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return output_variable;
|
||||
}
|
||||
|
||||
// Helper to get the ids of float constants 0.0 and 1.0, creating them if
|
||||
// necessary.
|
||||
std::pair<uint32_t, uint32_t> FindOrCreateFloatZeroAndOne(
|
||||
opt::IRContext* ir_context, opt::analysis::Float* float_type) {
|
||||
float one = 1.0;
|
||||
uint32_t one_as_uint;
|
||||
memcpy(&one_as_uint, &one, sizeof(float));
|
||||
std::vector<uint32_t> zero_bytes = {0};
|
||||
std::vector<uint32_t> one_bytes = {one_as_uint};
|
||||
auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant(
|
||||
MakeUnique<opt::analysis::FloatConstant>(float_type, zero_bytes));
|
||||
auto constant_one = ir_context->get_constant_mgr()->RegisterConstant(
|
||||
MakeUnique<opt::analysis::FloatConstant>(float_type, one_bytes));
|
||||
auto constant_zero_id = ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(constant_zero)
|
||||
->result_id();
|
||||
auto constant_one_id = ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(constant_one)
|
||||
->result_id();
|
||||
return std::pair<uint32_t, uint32_t>(constant_zero_id, constant_one_id);
|
||||
}
|
||||
|
||||
std::unique_ptr<TransformationReplaceConstantWithUniform>
|
||||
MakeConstantUniformReplacement(opt::IRContext* ir_context,
|
||||
const FactManager& fact_manager,
|
||||
uint32_t constant_id,
|
||||
uint32_t greater_than_instruction,
|
||||
uint32_t in_operand_index) {
|
||||
return MakeUnique<TransformationReplaceConstantWithUniform>(
|
||||
MakeIdUseDescriptor(constant_id,
|
||||
MakeInstructionDescriptor(greater_than_instruction,
|
||||
SpvOpFOrdGreaterThan, 0),
|
||||
in_operand_index),
|
||||
fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
|
||||
ir_context->TakeNextId(), ir_context->TakeNextId());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ForceRenderRed(
|
||||
const spv_target_env& target_env, spv_validator_options validator_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
std::vector<uint32_t>* binary_out) {
|
||||
auto message_consumer = spvtools::utils::CLIMessageConsumer;
|
||||
spvtools::SpirvTools tools(target_env);
|
||||
if (!tools.IsValid()) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) {
|
||||
message_consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
std::unique_ptr<opt::IRContext> ir_context = BuildModule(
|
||||
target_env, message_consumer, binary_in.data(), binary_in.size());
|
||||
assert(ir_context);
|
||||
|
||||
// Set up a fact manager with any given initial facts.
|
||||
FactManager fact_manager;
|
||||
for (auto& fact : initial_facts.fact()) {
|
||||
fact_manager.AddFact(fact, ir_context.get());
|
||||
}
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
validator_options);
|
||||
|
||||
auto entry_point_function =
|
||||
FindFragmentShaderEntryPoint(ir_context.get(), message_consumer);
|
||||
auto output_variable =
|
||||
FindVec4OutputVariable(ir_context.get(), message_consumer);
|
||||
if (entry_point_function == nullptr || output_variable == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
opt::analysis::Float temp_float_type(32);
|
||||
opt::analysis::Float* float_type = ir_context->get_type_mgr()
|
||||
->GetRegisteredType(&temp_float_type)
|
||||
->AsFloat();
|
||||
std::pair<uint32_t, uint32_t> zero_one_float_ids =
|
||||
FindOrCreateFloatZeroAndOne(ir_context.get(), float_type);
|
||||
|
||||
// Make the new exit block
|
||||
auto new_exit_block_id = ir_context->TakeNextId();
|
||||
{
|
||||
auto label = MakeUnique<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
|
||||
new_exit_block_id,
|
||||
opt::Instruction::OperandList());
|
||||
auto new_exit_block = MakeUnique<opt::BasicBlock>(std::move(label));
|
||||
new_exit_block->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
ir_context.get(), SpvOpReturn, 0, 0, opt::Instruction::OperandList()));
|
||||
entry_point_function->AddBasicBlock(std::move(new_exit_block));
|
||||
}
|
||||
|
||||
// Make the new entry block
|
||||
{
|
||||
auto label = MakeUnique<opt::Instruction>(ir_context.get(), SpvOpLabel, 0,
|
||||
ir_context->TakeNextId(),
|
||||
opt::Instruction::OperandList());
|
||||
auto new_entry_block = MakeUnique<opt::BasicBlock>(std::move(label));
|
||||
|
||||
// Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing
|
||||
// the colour red.
|
||||
opt::Operand zero_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.first}};
|
||||
opt::Operand one_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.second}};
|
||||
opt::Instruction::OperandList op_composite_construct_operands = {
|
||||
one_float, zero_float, zero_float, one_float};
|
||||
auto temp_vec4 = opt::analysis::Vector(float_type, 4);
|
||||
auto vec4_id = ir_context->get_type_mgr()->GetId(&temp_vec4);
|
||||
auto red = MakeUnique<opt::Instruction>(
|
||||
ir_context.get(), SpvOpCompositeConstruct, vec4_id,
|
||||
ir_context->TakeNextId(), op_composite_construct_operands);
|
||||
auto red_id = red->result_id();
|
||||
new_entry_block->AddInstruction(std::move(red));
|
||||
|
||||
// Make an instruction to store red into the output color.
|
||||
opt::Operand variable_to_store_into = {SPV_OPERAND_TYPE_ID,
|
||||
{output_variable->result_id()}};
|
||||
opt::Operand value_to_be_stored = {SPV_OPERAND_TYPE_ID, {red_id}};
|
||||
opt::Instruction::OperandList op_store_operands = {variable_to_store_into,
|
||||
value_to_be_stored};
|
||||
new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
ir_context.get(), SpvOpStore, 0, 0, op_store_operands));
|
||||
|
||||
// We are going to attempt to construct 'false' as an expression of the form
|
||||
// 'literal1 > literal2'. If we succeed, we will later replace each literal
|
||||
// with a uniform of the same value - we can only do that replacement once
|
||||
// we have added the entry block to the module.
|
||||
std::unique_ptr<TransformationReplaceConstantWithUniform>
|
||||
first_greater_then_operand_replacement = nullptr;
|
||||
std::unique_ptr<TransformationReplaceConstantWithUniform>
|
||||
second_greater_then_operand_replacement = nullptr;
|
||||
uint32_t id_guaranteed_to_be_false = 0;
|
||||
|
||||
opt::analysis::Bool temp_bool_type;
|
||||
opt::analysis::Bool* registered_bool_type =
|
||||
ir_context->get_type_mgr()
|
||||
->GetRegisteredType(&temp_bool_type)
|
||||
->AsBool();
|
||||
|
||||
auto float_type_id = ir_context->get_type_mgr()->GetId(float_type);
|
||||
auto types_for_which_uniforms_are_known =
|
||||
fact_manager.GetTypesForWhichUniformValuesAreKnown();
|
||||
|
||||
// Check whether we have any float uniforms.
|
||||
if (std::find(types_for_which_uniforms_are_known.begin(),
|
||||
types_for_which_uniforms_are_known.end(),
|
||||
float_type_id) != types_for_which_uniforms_are_known.end()) {
|
||||
// We have at least one float uniform; let's see whether we have at least
|
||||
// two.
|
||||
auto available_constants =
|
||||
fact_manager.GetConstantsAvailableFromUniformsForType(
|
||||
ir_context.get(), float_type_id);
|
||||
if (available_constants.size() > 1) {
|
||||
// Grab the float constants associated with the first two known float
|
||||
// uniforms.
|
||||
auto first_constant =
|
||||
ir_context->get_constant_mgr()
|
||||
->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
|
||||
available_constants[0]))
|
||||
->AsFloatConstant();
|
||||
auto second_constant =
|
||||
ir_context->get_constant_mgr()
|
||||
->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef(
|
||||
available_constants[1]))
|
||||
->AsFloatConstant();
|
||||
|
||||
// Now work out which of the two constants is larger than the other.
|
||||
uint32_t larger_constant_index = 0;
|
||||
uint32_t smaller_constant_index = 0;
|
||||
if (first_constant->GetFloat() > second_constant->GetFloat()) {
|
||||
larger_constant_index = 0;
|
||||
smaller_constant_index = 1;
|
||||
} else if (first_constant->GetFloat() < second_constant->GetFloat()) {
|
||||
larger_constant_index = 1;
|
||||
smaller_constant_index = 0;
|
||||
}
|
||||
|
||||
// Only proceed with these constants if they have turned out to be
|
||||
// distinct.
|
||||
if (larger_constant_index != smaller_constant_index) {
|
||||
// We are in a position to create 'false' as 'literal1 > literal2', so
|
||||
// reserve an id for this computation; this id will end up being
|
||||
// guaranteed to be 'false'.
|
||||
id_guaranteed_to_be_false = ir_context->TakeNextId();
|
||||
|
||||
auto smaller_constant = available_constants[smaller_constant_index];
|
||||
auto larger_constant = available_constants[larger_constant_index];
|
||||
|
||||
opt::Instruction::OperandList greater_than_operands = {
|
||||
{SPV_OPERAND_TYPE_ID, {smaller_constant}},
|
||||
{SPV_OPERAND_TYPE_ID, {larger_constant}}};
|
||||
new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
|
||||
ir_context.get(), SpvOpFOrdGreaterThan,
|
||||
ir_context->get_type_mgr()->GetId(registered_bool_type),
|
||||
id_guaranteed_to_be_false, greater_than_operands));
|
||||
|
||||
first_greater_then_operand_replacement =
|
||||
MakeConstantUniformReplacement(ir_context.get(), fact_manager,
|
||||
smaller_constant,
|
||||
id_guaranteed_to_be_false, 0);
|
||||
second_greater_then_operand_replacement =
|
||||
MakeConstantUniformReplacement(ir_context.get(), fact_manager,
|
||||
larger_constant,
|
||||
id_guaranteed_to_be_false, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (id_guaranteed_to_be_false == 0) {
|
||||
auto constant_false = ir_context->get_constant_mgr()->RegisterConstant(
|
||||
MakeUnique<opt::analysis::BoolConstant>(registered_bool_type, false));
|
||||
id_guaranteed_to_be_false = ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(constant_false)
|
||||
->result_id();
|
||||
}
|
||||
|
||||
opt::Operand false_condition = {SPV_OPERAND_TYPE_ID,
|
||||
{id_guaranteed_to_be_false}};
|
||||
opt::Operand then_block = {SPV_OPERAND_TYPE_ID,
|
||||
{entry_point_function->entry()->id()}};
|
||||
opt::Operand else_block = {SPV_OPERAND_TYPE_ID, {new_exit_block_id}};
|
||||
opt::Instruction::OperandList op_branch_conditional_operands = {
|
||||
false_condition, then_block, else_block};
|
||||
new_entry_block->AddInstruction(
|
||||
MakeUnique<opt::Instruction>(ir_context.get(), SpvOpBranchConditional,
|
||||
0, 0, op_branch_conditional_operands));
|
||||
|
||||
entry_point_function->InsertBasicBlockBefore(
|
||||
std::move(new_entry_block), entry_point_function->entry().get());
|
||||
|
||||
for (auto& replacement : {first_greater_then_operand_replacement.get(),
|
||||
second_greater_then_operand_replacement.get()}) {
|
||||
if (replacement) {
|
||||
assert(replacement->IsApplicable(ir_context.get(),
|
||||
transformation_context));
|
||||
replacement->Apply(ir_context.get(), &transformation_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write out the module as a binary.
|
||||
ir_context->module()->ToBinary(binary_out, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) 2019 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_FORCE_RENDER_RED_H_
|
||||
#define SOURCE_FORCE_RENDER_RED_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Requires |binary_in| to be a valid SPIR-V module with Shader capability,
|
||||
// containing an entry point with the Fragment execution model, and a single
|
||||
// output variable of type vec4.
|
||||
//
|
||||
// Turns the body of this entry point into effectively:
|
||||
//
|
||||
// output_variable = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
// if (false) {
|
||||
// original_body
|
||||
// }
|
||||
//
|
||||
// If suitable facts about values of uniforms are available, the 'false' will
|
||||
// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for
|
||||
// which it is known that 'u < v' holds.
|
||||
bool ForceRenderRed(
|
||||
const spv_target_env& target_env, spv_validator_options validator_options,
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
|
||||
std::vector<uint32_t>* binary_out);
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FORCE_RENDER_RED_H_
|
||||
394
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
394
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@@ -1,394 +0,0 @@
|
||||
// Copyright (c) 2019 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.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "source/fuzz/fact_manager/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
|
||||
#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_opphi_synonyms.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_parameters.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_stores.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_synonyms.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
|
||||
#include "source/fuzz/fuzzer_pass_construct_composites.h"
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
#include "source/fuzz/fuzzer_pass_donate_modules.h"
|
||||
#include "source/fuzz/fuzzer_pass_inline_functions.h"
|
||||
#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_adds_subs_muls_with_carrying_extended.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
|
||||
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/spirv_fuzzer_options.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kIdBoundGap = 100;
|
||||
|
||||
const uint32_t kTransformationLimit = 500;
|
||||
|
||||
const uint32_t kChanceOfApplyingAnotherPass = 85;
|
||||
|
||||
// A convenience method to add a fuzzer pass to |passes| with probability 0.5.
|
||||
// All fuzzer passes take |ir_context|, |transformation_context|,
|
||||
// |fuzzer_context| and |transformation_sequence_out| as parameters. Extra
|
||||
// arguments can be provided via |extra_args|.
|
||||
template <typename T, typename... Args>
|
||||
void MaybeAddPass(
|
||||
std::vector<std::unique_ptr<FuzzerPass>>* passes,
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformation_sequence_out,
|
||||
Args&&... extra_args) {
|
||||
if (fuzzer_context->ChooseEven()) {
|
||||
passes->push_back(MakeUnique<T>(ir_context, transformation_context,
|
||||
fuzzer_context, transformation_sequence_out,
|
||||
std::forward<Args>(extra_args)...));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Fuzzer::Fuzzer(spv_target_env target_env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options)
|
||||
: target_env_(target_env),
|
||||
seed_(seed),
|
||||
validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass),
|
||||
validator_options_(validator_options) {}
|
||||
|
||||
Fuzzer::~Fuzzer() = default;
|
||||
|
||||
void Fuzzer::SetMessageConsumer(MessageConsumer consumer) {
|
||||
consumer_ = std::move(consumer);
|
||||
}
|
||||
|
||||
bool Fuzzer::ApplyPassAndCheckValidity(
|
||||
FuzzerPass* pass, const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const {
|
||||
pass->Apply();
|
||||
if (validate_after_each_fuzzer_pass_) {
|
||||
std::vector<uint32_t> binary_to_validate;
|
||||
ir_context.module()->ToBinary(&binary_to_validate, false);
|
||||
if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
|
||||
validator_options_)) {
|
||||
consumer_(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during fuzzing (set a breakpoint to "
|
||||
"inspect); stopping.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const {
|
||||
// Check compatibility between the library version being linked with and the
|
||||
// header files being used.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
spvtools::SpirvTools tools(target_env_);
|
||||
tools.SetMessageConsumer(consumer_);
|
||||
if (!tools.IsValid()) {
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Failed to create SPIRV-Tools interface; stopping.");
|
||||
return Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface;
|
||||
}
|
||||
|
||||
// Initial binary should be valid.
|
||||
if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options_)) {
|
||||
consumer_(SPV_MSG_ERROR, nullptr, {},
|
||||
"Initial binary is invalid; stopping.");
|
||||
return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
|
||||
}
|
||||
|
||||
// Build the module from the input binary.
|
||||
std::unique_ptr<opt::IRContext> ir_context =
|
||||
BuildModule(target_env_, consumer_, binary_in.data(), binary_in.size());
|
||||
assert(ir_context);
|
||||
|
||||
// Make a PRNG from the seed passed to the fuzzer on creation.
|
||||
PseudoRandomGenerator random_generator(seed_);
|
||||
|
||||
// The fuzzer will introduce new ids into the module. The module's id bound
|
||||
// gives the smallest id that can be used for this purpose. We add an offset
|
||||
// to this so that there is a sizeable gap between the ids used in the
|
||||
// original module and the ids used for fuzzing, as a readability aid.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
|
||||
// case where the maximum id bound is reached.
|
||||
auto minimum_fresh_id = ir_context->module()->id_bound() + kIdBoundGap;
|
||||
FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id);
|
||||
|
||||
FactManager fact_manager;
|
||||
fact_manager.AddFacts(consumer_, initial_facts, ir_context.get());
|
||||
TransformationContext transformation_context(&fact_manager,
|
||||
validator_options_);
|
||||
|
||||
// Apply some semantics-preserving passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> passes;
|
||||
while (passes.empty()) {
|
||||
MaybeAddPass<FuzzerPassAddAccessChains>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddCompositeInserts>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddCompositeTypes>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddCopyMemory>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadBreaks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddDeadContinues>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddEquationInstructions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddFunctionCalls>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddGlobalVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
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<FuzzerPassAddOpPhiSynonyms>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddParameters>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddRelaxedDecorations>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddSynonyms>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassApplyIdSynonyms>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassConstructComposites>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassCopyObjects>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassDonateModules>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out, donor_suppliers);
|
||||
MaybeAddPass<FuzzerPassInlineFunctions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
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);
|
||||
MaybeAddPass<FuzzerPassObfuscateConstants>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassOutlineFunctions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermuteBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
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);
|
||||
MaybeAddPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSplitBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
}
|
||||
|
||||
bool is_first = true;
|
||||
while (static_cast<uint32_t>(
|
||||
transformation_sequence_out->transformation_size()) <
|
||||
kTransformationLimit &&
|
||||
(is_first ||
|
||||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
|
||||
is_first = false;
|
||||
if (!ApplyPassAndCheckValidity(
|
||||
passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
|
||||
tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
}
|
||||
|
||||
// Now apply some passes that it does not make sense to apply repeatedly,
|
||||
// as they do not unlock other passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
||||
MaybeAddPass<FuzzerPassAdjustBranchWeights>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustFunctionControls>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustLoopControls>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermutePhiOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
for (auto& pass : final_passes) {
|
||||
if (!ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the module as a binary.
|
||||
ir_context->module()->ToBinary(binary_out, false);
|
||||
|
||||
return Fuzzer::FuzzerResultStatus::kComplete;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
101
3rdparty/spirv-tools/source/fuzz/fuzzer.h
vendored
101
3rdparty/spirv-tools/source/fuzz/fuzzer.h
vendored
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) 2019 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_H_
|
||||
#define SOURCE_FUZZ_FUZZER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by
|
||||
// running a number of randomized fuzzer passes.
|
||||
class Fuzzer {
|
||||
public:
|
||||
// Possible statuses that can result from running the fuzzer.
|
||||
enum class FuzzerResultStatus {
|
||||
kComplete,
|
||||
kFailedToCreateSpirvToolsInterface,
|
||||
kFuzzerPassLedToInvalidModule,
|
||||
kInitialBinaryInvalid,
|
||||
};
|
||||
|
||||
// Constructs a fuzzer from the given target environment |target_env|. |seed|
|
||||
// is a seed for pseudo-random number generation.
|
||||
// |validate_after_each_fuzzer_pass| controls whether the validator will be
|
||||
// invoked after every fuzzer pass is applied.
|
||||
Fuzzer(spv_target_env target_env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass,
|
||||
spv_validator_options validator_options);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Fuzzer(const Fuzzer&) = delete;
|
||||
Fuzzer(Fuzzer&&) = delete;
|
||||
Fuzzer& operator=(const Fuzzer&) = delete;
|
||||
Fuzzer& operator=(Fuzzer&&) = delete;
|
||||
|
||||
~Fuzzer();
|
||||
|
||||
// Sets the message consumer to the given |consumer|. The |consumer| will be
|
||||
// invoked once for each message communicated from the library.
|
||||
void SetMessageConsumer(MessageConsumer consumer);
|
||||
|
||||
// Transforms |binary_in| to |binary_out| by running a number of randomized
|
||||
// fuzzer passes. Initial facts about the input binary and the context in
|
||||
// which it will execute are provided via |initial_facts|. A source of donor
|
||||
// modules to be used by transformations is provided via |donor_suppliers|.
|
||||
// The transformation sequence that was applied is returned via
|
||||
// |transformation_sequence_out|.
|
||||
FuzzerResultStatus Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
// Applies |pass|, which must be a pass constructed with |ir_context|, and
|
||||
// then returns true if and only if |ir_context| is valid. |tools| is used to
|
||||
// check validity.
|
||||
bool ApplyPassAndCheckValidity(FuzzerPass* pass,
|
||||
const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const;
|
||||
|
||||
// Target environment.
|
||||
const spv_target_env target_env_;
|
||||
|
||||
// Message consumer.
|
||||
MessageConsumer consumer_;
|
||||
|
||||
// Seed for random number generator.
|
||||
const uint32_t seed_;
|
||||
|
||||
// Determines whether the validator should be invoked after every fuzzer pass.
|
||||
bool validate_after_each_fuzzer_pass_;
|
||||
|
||||
// Options to control validation.
|
||||
spv_validator_options validator_options_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_H_
|
||||
320
3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
vendored
320
3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
vendored
@@ -1,320 +0,0 @@
|
||||
// Copyright (c) 2019 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_context.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
// Default <minimum, maximum> pairs of probabilities for applying various
|
||||
// transformations. All values are percentages. Keep them in alphabetical order.
|
||||
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
|
||||
90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingCompositeInsert = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
|
||||
90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
|
||||
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};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingOpPhiSynonym = {5, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
|
||||
70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingMemoryOperandsMask = {20,
|
||||
90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
|
||||
90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfCallingFunction = {1, 10};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
|
||||
20, 80};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
|
||||
50, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperToInsertInComposite = {
|
||||
30, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
|
||||
{50, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInliningFunction = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
|
||||
10, 90};
|
||||
const std::pair<uint32_t, uint32_t>
|
||||
kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90};
|
||||
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, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t>
|
||||
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
||||
30, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
|
||||
20, 40};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
|
||||
{10, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
|
||||
20, 90};
|
||||
|
||||
// Default limits for various quantities that are chosen during fuzzing.
|
||||
// Keep them in alphabetical order.
|
||||
const uint32_t kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure = 1000;
|
||||
const uint32_t kDefaultMaxLoopControlPartialCount = 100;
|
||||
const uint32_t kDefaultMaxLoopControlPeelCount = 100;
|
||||
const uint32_t kDefaultMaxLoopLimit = 20;
|
||||
const uint32_t kDefaultMaxNewArraySizeLimit = 100;
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
|
||||
// think whether there is a better limit on the maximum number of parameters.
|
||||
const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
|
||||
const uint32_t kDefaultMaxNumberOfNewParameters = 15;
|
||||
const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5;
|
||||
|
||||
// Default functions for controlling how deep to go during recursive
|
||||
// generation/transformation. Keep them in alphabetical order.
|
||||
|
||||
const std::function<bool(uint32_t, RandomGenerator*)>
|
||||
kDefaultGoDeeperInConstantObfuscation =
|
||||
[](uint32_t current_depth, RandomGenerator* random_generator) -> bool {
|
||||
double chance = 1.0 / std::pow(3.0, static_cast<float>(current_depth + 1));
|
||||
return random_generator->RandomDouble() < chance;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
uint32_t min_fresh_id)
|
||||
: random_generator_(random_generator),
|
||||
next_fresh_id_(min_fresh_id),
|
||||
max_equivalence_class_size_for_data_synonym_fact_closure_(
|
||||
kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure),
|
||||
max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount),
|
||||
max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
|
||||
max_loop_limit_(kDefaultMaxLoopLimit),
|
||||
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
|
||||
max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
|
||||
max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
|
||||
max_number_of_parameters_replaced_with_struct_(
|
||||
kGetDefaultMaxNumberOfParametersReplacedWithStruct),
|
||||
go_deeper_in_constant_obfuscation_(
|
||||
kDefaultGoDeeperInConstantObfuscation) {
|
||||
chance_of_adding_access_chain_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingAccessChain);
|
||||
chance_of_adding_another_struct_field_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
|
||||
chance_of_adding_array_or_struct_type_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
|
||||
chance_of_adding_composite_insert_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert);
|
||||
chance_of_adding_copy_memory_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory);
|
||||
chance_of_adding_dead_block_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
|
||||
chance_of_adding_dead_break_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
|
||||
chance_of_adding_dead_continue_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
|
||||
chance_of_adding_equation_instruction_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction);
|
||||
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_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
|
||||
chance_of_adding_matrix_type_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
|
||||
chance_of_adding_no_contraction_decoration_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
|
||||
chance_of_adding_opphi_synonym_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingOpPhiSynonym);
|
||||
chance_of_adding_parameters =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingParameters);
|
||||
chance_of_adding_relaxed_decoration_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
|
||||
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
|
||||
chance_of_adding_vector_shuffle_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
|
||||
chance_of_adding_vector_type_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
|
||||
chance_of_adjusting_branch_weights_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
|
||||
chance_of_adjusting_function_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
|
||||
chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms);
|
||||
chance_of_adjusting_loop_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
|
||||
chance_of_adjusting_memory_operands_mask_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
|
||||
chance_of_adjusting_selection_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
|
||||
chance_of_calling_function_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfCallingFunction);
|
||||
chance_of_choosing_struct_type_vs_array_type_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
|
||||
chance_of_choosing_workgroup_storage_class_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass);
|
||||
chance_of_constructing_composite_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
|
||||
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
|
||||
chance_of_donating_additional_module_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
|
||||
chance_of_going_deeper_to_insert_in_composite_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite);
|
||||
chance_of_going_deeper_when_making_access_chain_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
|
||||
chance_of_inlining_function_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInliningFunction);
|
||||
chance_of_interchanging_signedness_of_integer_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands);
|
||||
chance_of_interchanging_zero_like_constants_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
|
||||
chance_of_inverting_comparison_operators_ =
|
||||
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);
|
||||
chance_of_obfuscating_constant_ =
|
||||
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_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad);
|
||||
chance_of_replacing_id_with_synonym_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
|
||||
chance_of_replacing_linear_algebra_instructions_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
||||
chance_of_replacing_load_store_with_copy_memory_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
||||
chance_of_replacing_parameters_with_globals_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
||||
chance_of_replacing_parameters_with_struct_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
|
||||
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
|
||||
chance_of_swapping_conditional_branch_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
|
||||
chance_of_toggling_access_chain_instruction_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
|
||||
}
|
||||
|
||||
FuzzerContext::~FuzzerContext() = default;
|
||||
|
||||
uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; }
|
||||
|
||||
std::vector<uint32_t> FuzzerContext::GetFreshIds(const uint32_t count) {
|
||||
std::vector<uint32_t> fresh_ids(count);
|
||||
|
||||
for (uint32_t& fresh_id : fresh_ids) {
|
||||
fresh_id = next_fresh_id_++;
|
||||
}
|
||||
|
||||
return fresh_ids;
|
||||
}
|
||||
|
||||
bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); }
|
||||
|
||||
bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) {
|
||||
assert(percentage_chance <= 100);
|
||||
return random_generator_->RandomPercentage() < percentage_chance;
|
||||
}
|
||||
|
||||
uint32_t FuzzerContext::ChooseBetweenMinAndMax(
|
||||
const std::pair<uint32_t, uint32_t>& min_max) {
|
||||
assert(min_max.first <= min_max.second);
|
||||
return min_max.first +
|
||||
random_generator_->RandomUint32(min_max.second - min_max.first + 1);
|
||||
}
|
||||
|
||||
protobufs::TransformationAddSynonym::SynonymType
|
||||
FuzzerContext::GetRandomSynonymType() {
|
||||
// value_count method is guaranteed to return a value greater than 0.
|
||||
auto result_index = ChooseBetweenMinAndMax(
|
||||
{0, static_cast<uint32_t>(
|
||||
protobufs::TransformationAddSynonym::SynonymType_descriptor()
|
||||
->value_count() -
|
||||
1)});
|
||||
auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor()
|
||||
->value(result_index)
|
||||
->number();
|
||||
assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) &&
|
||||
"|result| is not a value of SynonymType");
|
||||
return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
445
3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
vendored
445
3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
vendored
@@ -1,445 +0,0 @@
|
||||
// Copyright (c) 2019 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_CONTEXT_H_
|
||||
#define SOURCE_FUZZ_FUZZER_CONTEXT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/random_generator.h"
|
||||
#include "source/opt/function.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Encapsulates all parameters that control the fuzzing process, such as the
|
||||
// source of randomness and the probabilities with which transformations are
|
||||
// applied.
|
||||
class FuzzerContext {
|
||||
public:
|
||||
// Constructs a fuzzer context with a given random generator and the minimum
|
||||
// value that can be used for fresh ids.
|
||||
FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id);
|
||||
|
||||
~FuzzerContext();
|
||||
|
||||
// Returns a random boolean.
|
||||
bool ChooseEven();
|
||||
|
||||
// Returns true if and only if a randomly-chosen integer in the range [0, 100]
|
||||
// is less than |percentage_chance|.
|
||||
bool ChoosePercentage(uint32_t percentage_chance);
|
||||
|
||||
// Returns a random index into |sequence|, which is expected to have a 'size'
|
||||
// method, and which must be non-empty. Typically 'HasSizeMethod' will be an
|
||||
// std::vector.
|
||||
template <typename HasSizeMethod>
|
||||
uint32_t RandomIndex(const HasSizeMethod& sequence) const {
|
||||
assert(sequence.size() > 0);
|
||||
return random_generator_->RandomUint32(
|
||||
static_cast<uint32_t>(sequence.size()));
|
||||
}
|
||||
|
||||
// Selects a random index into |sequence|, removes the element at that index
|
||||
// and returns it.
|
||||
template <typename T>
|
||||
T RemoveAtRandomIndex(std::vector<T>* sequence) const {
|
||||
uint32_t index = RandomIndex(*sequence);
|
||||
T result = sequence->at(index);
|
||||
sequence->erase(sequence->begin() + index);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
|
||||
// |lo| and |hi| must be valid indices to the |sequence|
|
||||
template <typename T>
|
||||
void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
|
||||
auto& array = *sequence;
|
||||
|
||||
if (array.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid");
|
||||
|
||||
// i > lo to account for potential infinite loop when lo == 0
|
||||
for (size_t i = hi; i > lo; --i) {
|
||||
auto index =
|
||||
random_generator_->RandomUint32(static_cast<uint32_t>(i - lo + 1));
|
||||
|
||||
if (lo + index != i) {
|
||||
// Introduce std::swap to the scope but don't use it
|
||||
// directly since there might be a better overload
|
||||
using std::swap;
|
||||
swap(array[lo + index], array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ramdomly shuffles a |sequence|
|
||||
template <typename T>
|
||||
void Shuffle(std::vector<T>* sequence) const {
|
||||
if (!sequence->empty()) {
|
||||
Shuffle(sequence, 0, sequence->size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Yields an id that is guaranteed not to be used in the module being fuzzed,
|
||||
// or to have been issued before.
|
||||
uint32_t GetFreshId();
|
||||
|
||||
// Returns a vector of |count| fresh ids.
|
||||
std::vector<uint32_t> GetFreshIds(const uint32_t count);
|
||||
|
||||
// Probabilities associated with applying various transformations.
|
||||
// Keep them in alphabetical order.
|
||||
uint32_t GetChanceOfAddingAccessChain() {
|
||||
return chance_of_adding_access_chain_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingAnotherStructField() {
|
||||
return chance_of_adding_another_struct_field_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingArrayOrStructType() {
|
||||
return chance_of_adding_array_or_struct_type_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingCompositeInsert() {
|
||||
return chance_of_adding_composite_insert_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingCopyMemory() {
|
||||
return chance_of_adding_copy_memory_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
|
||||
uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
|
||||
uint32_t GetChanceOfAddingDeadContinue() {
|
||||
return chance_of_adding_dead_continue_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingEquationInstruction() {
|
||||
return chance_of_adding_equation_instruction_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingGlobalVariable() {
|
||||
return chance_of_adding_global_variable_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
|
||||
return chance_of_adding_image_sample_unused_components_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
|
||||
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_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingNoContractionDecoration() {
|
||||
return chance_of_adding_no_contraction_decoration_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingOpPhiSynonym() {
|
||||
return chance_of_adding_opphi_synonym_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
|
||||
uint32_t GetChanceOfAddingRelaxedDecoration() {
|
||||
return chance_of_adding_relaxed_decoration_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
|
||||
uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
|
||||
uint32_t GetChanceOfAddingVectorShuffle() {
|
||||
return chance_of_adding_vector_shuffle_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingVectorType() {
|
||||
return chance_of_adding_vector_type_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingBranchWeights() {
|
||||
return chance_of_adjusting_branch_weights_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingFunctionControl() {
|
||||
return chance_of_adjusting_function_control_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingLoopControl() {
|
||||
return chance_of_adjusting_loop_control_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingMemoryOperandsMask() {
|
||||
return chance_of_adjusting_memory_operands_mask_;
|
||||
}
|
||||
uint32_t GetChanceOfAdjustingSelectionControl() {
|
||||
return chance_of_adjusting_selection_control_;
|
||||
}
|
||||
uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; }
|
||||
uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
|
||||
return chance_of_choosing_struct_type_vs_array_type_;
|
||||
}
|
||||
uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
|
||||
return chance_of_choosing_workgroup_storage_class_;
|
||||
}
|
||||
uint32_t GetChanceOfConstructingComposite() {
|
||||
return chance_of_constructing_composite_;
|
||||
}
|
||||
uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
|
||||
uint32_t GetChanceOfDonatingAdditionalModule() {
|
||||
return chance_of_donating_additional_module_;
|
||||
}
|
||||
uint32_t GetChanceOfGoingDeeperToInsertInComposite() {
|
||||
return chance_of_going_deeper_to_insert_in_composite_;
|
||||
}
|
||||
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
|
||||
return chance_of_going_deeper_when_making_access_chain_;
|
||||
}
|
||||
uint32_t GetChanceOfInliningFunction() {
|
||||
return chance_of_inlining_function_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
|
||||
return chance_of_interchanging_signedness_of_integer_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingZeroLikeConstants() {
|
||||
return chance_of_interchanging_zero_like_constants_;
|
||||
}
|
||||
uint32_t GetChanceOfInvertingComparisonOperators() {
|
||||
return chance_of_inverting_comparison_operators_;
|
||||
}
|
||||
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() {
|
||||
return chance_of_obfuscating_constant_;
|
||||
}
|
||||
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_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
|
||||
return chance_of_replacing_copyobject_with_store_load_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingIdWithSynonym() {
|
||||
return chance_of_replacing_id_with_synonym_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
|
||||
return chance_of_replacing_linear_algebra_instructions_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
|
||||
return chance_of_replacing_load_store_with_copy_memory_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
||||
return chance_of_replacing_parameters_with_globals_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingParametersWithStruct() {
|
||||
return chance_of_replacing_parameters_with_struct_;
|
||||
}
|
||||
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
|
||||
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
|
||||
return chance_of_swapping_conditional_branch_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfTogglingAccessChainInstruction() {
|
||||
return chance_of_toggling_access_chain_instruction_;
|
||||
}
|
||||
|
||||
// Other functions to control transformations. Keep them in alphabetical
|
||||
// order.
|
||||
uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
|
||||
return max_equivalence_class_size_for_data_synonym_fact_closure_;
|
||||
}
|
||||
uint32_t GetMaximumNumberOfFunctionParameters() {
|
||||
return max_number_of_function_parameters_;
|
||||
}
|
||||
uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
|
||||
return max_number_of_parameters_replaced_with_struct_;
|
||||
}
|
||||
std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
|
||||
std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
|
||||
|
||||
while (branch_weights.first == 0 && branch_weights.second == 0) {
|
||||
// Using INT32_MAX to do not overflow UINT32_MAX when the branch weights
|
||||
// are added together.
|
||||
branch_weights.first = random_generator_->RandomUint32(INT32_MAX);
|
||||
branch_weights.second = random_generator_->RandomUint32(INT32_MAX);
|
||||
}
|
||||
|
||||
return branch_weights;
|
||||
}
|
||||
std::vector<uint32_t> GetRandomComponentsForVectorShuffle(
|
||||
uint32_t max_component_index) {
|
||||
// Component count must be in range [2, 4].
|
||||
std::vector<uint32_t> components(random_generator_->RandomUint32(2) + 2);
|
||||
|
||||
for (uint32_t& component : components) {
|
||||
component = random_generator_->RandomUint32(max_component_index);
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
|
||||
return random_generator_->RandomUint32(composite_size_bound);
|
||||
}
|
||||
uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) {
|
||||
return random_generator_->RandomUint32(number_of_components);
|
||||
}
|
||||
uint32_t GetRandomLoopControlPartialCount() {
|
||||
return random_generator_->RandomUint32(max_loop_control_partial_count_);
|
||||
}
|
||||
uint32_t GetRandomLoopControlPeelCount() {
|
||||
return random_generator_->RandomUint32(max_loop_control_peel_count_);
|
||||
}
|
||||
uint32_t GetRandomLoopLimit() {
|
||||
return random_generator_->RandomUint32(max_loop_limit_);
|
||||
}
|
||||
uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
|
||||
assert(num_of_params < GetMaximumNumberOfFunctionParameters());
|
||||
return ChooseBetweenMinAndMax(
|
||||
{1, std::min(max_number_of_new_parameters_,
|
||||
GetMaximumNumberOfFunctionParameters() - num_of_params)});
|
||||
}
|
||||
uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) {
|
||||
assert(num_params != 0 && "A function must have parameters to replace");
|
||||
return ChooseBetweenMinAndMax(
|
||||
{1, std::min(num_params,
|
||||
GetMaximumNumberOfParametersReplacedWithStruct())});
|
||||
}
|
||||
uint32_t GetRandomSizeForNewArray() {
|
||||
// Ensure that the array size is non-zero.
|
||||
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
|
||||
}
|
||||
protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType();
|
||||
uint32_t GetRandomUnusedComponentCountForImageSample(
|
||||
uint32_t max_unused_component_count) {
|
||||
// Ensure that the number of unused components is non-zero.
|
||||
return random_generator_->RandomUint32(max_unused_component_count) + 1;
|
||||
}
|
||||
bool GoDeeperInConstantObfuscation(uint32_t depth) {
|
||||
return go_deeper_in_constant_obfuscation_(depth, random_generator_);
|
||||
}
|
||||
|
||||
private:
|
||||
// The source of randomness.
|
||||
RandomGenerator* random_generator_;
|
||||
// The next fresh id to be issued.
|
||||
uint32_t next_fresh_id_;
|
||||
|
||||
// Probabilities associated with applying various transformations.
|
||||
// Keep them in alphabetical order.
|
||||
uint32_t chance_of_adding_access_chain_;
|
||||
uint32_t chance_of_adding_another_struct_field_;
|
||||
uint32_t chance_of_adding_array_or_struct_type_;
|
||||
uint32_t chance_of_adding_composite_insert_;
|
||||
uint32_t chance_of_adding_copy_memory_;
|
||||
uint32_t chance_of_adding_dead_block_;
|
||||
uint32_t chance_of_adding_dead_break_;
|
||||
uint32_t chance_of_adding_dead_continue_;
|
||||
uint32_t chance_of_adding_equation_instruction_;
|
||||
uint32_t chance_of_adding_global_variable_;
|
||||
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_opphi_synonym_;
|
||||
uint32_t chance_of_adding_parameters;
|
||||
uint32_t chance_of_adding_relaxed_decoration_;
|
||||
uint32_t chance_of_adding_store_;
|
||||
uint32_t chance_of_adding_synonyms_;
|
||||
uint32_t chance_of_adding_vector_shuffle_;
|
||||
uint32_t chance_of_adding_vector_type_;
|
||||
uint32_t chance_of_adjusting_branch_weights_;
|
||||
uint32_t chance_of_adjusting_function_control_;
|
||||
uint32_t chance_of_adjusting_loop_control_;
|
||||
uint32_t chance_of_adjusting_memory_operands_mask_;
|
||||
uint32_t chance_of_adjusting_selection_control_;
|
||||
uint32_t chance_of_calling_function_;
|
||||
uint32_t chance_of_choosing_struct_type_vs_array_type_;
|
||||
uint32_t chance_of_choosing_workgroup_storage_class_;
|
||||
uint32_t chance_of_constructing_composite_;
|
||||
uint32_t chance_of_copying_object_;
|
||||
uint32_t chance_of_donating_additional_module_;
|
||||
uint32_t chance_of_going_deeper_to_insert_in_composite_;
|
||||
uint32_t chance_of_going_deeper_when_making_access_chain_;
|
||||
uint32_t chance_of_inlining_function_;
|
||||
uint32_t chance_of_interchanging_signedness_of_integer_operands_;
|
||||
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_;
|
||||
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
||||
uint32_t chance_of_replacing_load_store_with_copy_memory_;
|
||||
uint32_t chance_of_replacing_parameters_with_globals_;
|
||||
uint32_t chance_of_replacing_parameters_with_struct_;
|
||||
uint32_t chance_of_splitting_block_;
|
||||
uint32_t chance_of_swapping_conditional_branch_operands_;
|
||||
uint32_t chance_of_toggling_access_chain_instruction_;
|
||||
|
||||
// Limits associated with various quantities for which random values are
|
||||
// chosen during fuzzing.
|
||||
// Keep them in alphabetical order.
|
||||
uint32_t max_equivalence_class_size_for_data_synonym_fact_closure_;
|
||||
uint32_t max_loop_control_partial_count_;
|
||||
uint32_t max_loop_control_peel_count_;
|
||||
uint32_t max_loop_limit_;
|
||||
uint32_t max_new_array_size_limit_;
|
||||
uint32_t max_number_of_function_parameters_;
|
||||
uint32_t max_number_of_new_parameters_;
|
||||
uint32_t max_number_of_parameters_replaced_with_struct_;
|
||||
|
||||
// Functions to determine with what probability to go deeper when generating
|
||||
// or mutating constructs recursively.
|
||||
const std::function<bool(uint32_t, RandomGenerator*)>&
|
||||
go_deeper_in_constant_obfuscation_;
|
||||
|
||||
// Requires |min_max.first| <= |min_max.second|, and returns a value in the
|
||||
// range [ |min_max.first|, |min_max.second| ]
|
||||
uint32_t ChooseBetweenMinAndMax(const std::pair<uint32_t, uint32_t>& min_max);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_CONTEXT_H_
|
||||
715
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
715
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@@ -1,715 +0,0 @@
|
||||
// Copyright (c) 2019 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.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
#include "source/fuzz/transformation_add_constant_composite.h"
|
||||
#include "source/fuzz/transformation_add_constant_null.h"
|
||||
#include "source/fuzz/transformation_add_constant_scalar.h"
|
||||
#include "source/fuzz/transformation_add_global_undef.h"
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_loop_preheader.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
#include "source/fuzz/transformation_add_type_function.h"
|
||||
#include "source/fuzz/transformation_add_type_int.h"
|
||||
#include "source/fuzz/transformation_add_type_matrix.h"
|
||||
#include "source/fuzz/transformation_add_type_pointer.h"
|
||||
#include "source/fuzz/transformation_add_type_struct.h"
|
||||
#include "source/fuzz/transformation_add_type_vector.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPass::FuzzerPass(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: ir_context_(ir_context),
|
||||
transformation_context_(transformation_context),
|
||||
fuzzer_context_(fuzzer_context),
|
||||
transformations_(transformations) {}
|
||||
|
||||
FuzzerPass::~FuzzerPass() = default;
|
||||
|
||||
std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
|
||||
opt::Function* function, opt::BasicBlock* block,
|
||||
const opt::BasicBlock::iterator& inst_it,
|
||||
std::function<bool(opt::IRContext*, opt::Instruction*)>
|
||||
instruction_is_relevant) const {
|
||||
// TODO(afd) The following is (relatively) simple, but may end up being
|
||||
// prohibitively inefficient, as it walks the whole dominator tree for
|
||||
// every instruction that is considered.
|
||||
|
||||
std::vector<opt::Instruction*> result;
|
||||
// Consider all global declarations
|
||||
for (auto& global : GetIRContext()->module()->types_values()) {
|
||||
if (instruction_is_relevant(GetIRContext(), &global)) {
|
||||
result.push_back(&global);
|
||||
}
|
||||
}
|
||||
|
||||
// Consider all function parameters
|
||||
function->ForEachParam(
|
||||
[this, &instruction_is_relevant, &result](opt::Instruction* param) {
|
||||
if (instruction_is_relevant(GetIRContext(), param)) {
|
||||
result.push_back(param);
|
||||
}
|
||||
});
|
||||
|
||||
// Consider all previous instructions in this block
|
||||
for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
|
||||
++prev_inst_it) {
|
||||
if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) {
|
||||
result.push_back(&*prev_inst_it);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the dominator tree to consider all instructions from dominating
|
||||
// blocks
|
||||
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
|
||||
for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
|
||||
next_dominator != nullptr;
|
||||
next_dominator =
|
||||
dominator_analysis->ImmediateDominator(next_dominator)) {
|
||||
for (auto& dominating_inst : *next_dominator) {
|
||||
if (instruction_is_relevant(GetIRContext(), &dominating_inst)) {
|
||||
result.push_back(&dominating_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
|
||||
std::function<
|
||||
void(opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)>
|
||||
action) {
|
||||
// Consider every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// We now consider every instruction in the block, randomly deciding
|
||||
// whether to apply a transformation before it.
|
||||
|
||||
// In order for transformations to insert new instructions, they need to
|
||||
// be able to identify the instruction to insert before. We describe an
|
||||
// instruction via its opcode, 'opc', a base instruction 'base' that has a
|
||||
// result id, and the number of instructions with opcode 'opc' that we
|
||||
// should skip when searching from 'base' for the desired instruction.
|
||||
// (An instruction that has a result id is represented by its own opcode,
|
||||
// itself as 'base', and a skip-count of 0.)
|
||||
std::vector<std::tuple<uint32_t, SpvOp, uint32_t>>
|
||||
base_opcode_skip_triples;
|
||||
|
||||
// The initial base instruction is the block label.
|
||||
uint32_t base = block.id();
|
||||
|
||||
// Counts the number of times we have seen each opcode since we reset the
|
||||
// base instruction.
|
||||
std::map<SpvOp, uint32_t> skip_count;
|
||||
|
||||
// Consider every instruction in the block. The label is excluded: it is
|
||||
// only necessary to consider it as a base in case the first instruction
|
||||
// in the block does not have a result id.
|
||||
for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
|
||||
if (inst_it->HasResultId()) {
|
||||
// In the case that the instruction has a result id, we use the
|
||||
// instruction as its own base, and clear the skip counts we have
|
||||
// collected.
|
||||
base = inst_it->result_id();
|
||||
skip_count.clear();
|
||||
}
|
||||
const SpvOp opcode = inst_it->opcode();
|
||||
|
||||
// Invoke the provided function, which might apply a transformation.
|
||||
action(&function, &block, inst_it,
|
||||
MakeInstructionDescriptor(
|
||||
base, opcode,
|
||||
skip_count.count(opcode) ? skip_count.at(opcode) : 0));
|
||||
|
||||
if (!inst_it->HasResultId()) {
|
||||
skip_count[opcode] =
|
||||
skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateBoolType() {
|
||||
if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypeBoolean(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
|
||||
opt::analysis::Integer int_type(width, is_signed);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypeInt(result, width, is_signed));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
|
||||
opt::analysis::Float float_type(width);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypeFloat(result, width));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateFunctionType(
|
||||
uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
|
||||
// FindFunctionType has a sigle argument for OpTypeFunction operands
|
||||
// so we will have to copy them all in this vector
|
||||
std::vector<uint32_t> type_ids(argument_id.size() + 1);
|
||||
type_ids[0] = return_type_id;
|
||||
std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1);
|
||||
|
||||
// Check if type exists
|
||||
auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(
|
||||
TransformationAddTypeFunction(result, return_type_id, argument_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
|
||||
uint32_t component_count) {
|
||||
assert(component_count >= 2 && component_count <= 4 &&
|
||||
"Precondition: component count must be in range [2, 4].");
|
||||
opt::analysis::Type* component_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(component_type_id);
|
||||
assert(component_type && "Precondition: the component type must exist.");
|
||||
opt::analysis::Vector vector_type(component_type, component_count);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(
|
||||
TransformationAddTypeVector(result, component_type_id, component_count));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count,
|
||||
uint32_t row_count) {
|
||||
assert(column_count >= 2 && column_count <= 4 &&
|
||||
"Precondition: column count must be in range [2, 4].");
|
||||
assert(row_count >= 2 && row_count <= 4 &&
|
||||
"Precondition: row count must be in range [2, 4].");
|
||||
uint32_t column_type_id =
|
||||
FindOrCreateVectorType(FindOrCreateFloatType(32), row_count);
|
||||
opt::analysis::Type* column_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(column_type_id);
|
||||
opt::analysis::Matrix matrix_type(column_type, column_count);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(
|
||||
TransformationAddTypeMatrix(result, column_type_id, column_count));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateStructType(
|
||||
const std::vector<uint32_t>& component_type_ids) {
|
||||
if (auto existing_id =
|
||||
fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) {
|
||||
return existing_id;
|
||||
}
|
||||
auto new_id = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids));
|
||||
return new_id;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
|
||||
SpvStorageClass storage_class) {
|
||||
// We do not use the type manager here, due to problems related to isomorphic
|
||||
// but distinct structs not being regarded as different.
|
||||
auto existing_id = fuzzerutil::MaybeGetPointerType(
|
||||
GetIRContext(), base_type_id, storage_class);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(
|
||||
TransformationAddTypePointer(result, storage_class, base_type_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
|
||||
uint32_t width, bool is_signed, SpvStorageClass storage_class) {
|
||||
return FindOrCreatePointerType(FindOrCreateIntegerType(width, is_signed),
|
||||
storage_class);
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateIntegerConstant(
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
|
||||
bool is_irrelevant) {
|
||||
auto int_type_id = FindOrCreateIntegerType(width, is_signed);
|
||||
if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
|
||||
GetIRContext(), *GetTransformationContext(), words, int_type_id,
|
||||
is_irrelevant)) {
|
||||
return constant_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantScalar(result, int_type_id,
|
||||
words, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateFloatConstant(
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
|
||||
auto float_type_id = FindOrCreateFloatType(width);
|
||||
if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
|
||||
GetIRContext(), *GetTransformationContext(), words, float_type_id,
|
||||
is_irrelevant)) {
|
||||
return constant_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantScalar(result, float_type_id,
|
||||
words, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) {
|
||||
auto bool_type_id = FindOrCreateBoolType();
|
||||
if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
|
||||
GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u},
|
||||
bool_type_id, is_irrelevant)) {
|
||||
return constant_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(
|
||||
TransformationAddConstantBoolean(result, value, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t type_id,
|
||||
bool is_irrelevant) {
|
||||
assert(type_id && "Constant's type id can't be 0.");
|
||||
|
||||
const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
|
||||
assert(type && "Type does not exist.");
|
||||
|
||||
if (type->AsBool()) {
|
||||
assert(words.size() == 1);
|
||||
return FindOrCreateBoolConstant(words[0], is_irrelevant);
|
||||
} else if (const auto* integer = type->AsInteger()) {
|
||||
return FindOrCreateIntegerConstant(words, integer->width(),
|
||||
integer->IsSigned(), is_irrelevant);
|
||||
} else if (const auto* floating = type->AsFloat()) {
|
||||
return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant);
|
||||
}
|
||||
|
||||
// This assertion will fail in debug build but not in release build
|
||||
// so we return 0 to make compiler happy.
|
||||
assert(false && "Constant type is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateCompositeConstant(
|
||||
const std::vector<uint32_t>& component_ids, uint32_t type_id,
|
||||
bool is_irrelevant) {
|
||||
if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant(
|
||||
GetIRContext(), *GetTransformationContext(), component_ids, type_id,
|
||||
is_irrelevant)) {
|
||||
return existing_constant;
|
||||
}
|
||||
uint32_t result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantComposite(
|
||||
result, type_id, component_ids, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
|
||||
return inst.result_id();
|
||||
}
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddGlobalUndef(result, type_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
|
||||
// Find existing declaration
|
||||
opt::analysis::NullConstant null_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(type_id));
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
|
||||
|
||||
// Return if found
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
}
|
||||
|
||||
// Create new if not found
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantNull(result, type_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||
SpvStorageClass storage_class) const {
|
||||
// Records all of the basic types available in the module.
|
||||
std::set<uint32_t> basic_types;
|
||||
|
||||
// For each basic type, records all the associated pointer types that target
|
||||
// the basic type and that have |storage_class| as their storage class.
|
||||
std::map<uint32_t, std::vector<uint32_t>> basic_type_to_pointers;
|
||||
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
// For each basic type that we come across, record type, and the fact that
|
||||
// we cannot yet have seen any pointers that use the basic type as its
|
||||
// pointee type.
|
||||
//
|
||||
// For pointer types with basic pointee types, associate the pointer type
|
||||
// with the basic type.
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
// These are all basic types.
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
break;
|
||||
case SpvOpTypeArray:
|
||||
// An array type is basic if its base type is basic.
|
||||
if (basic_types.count(inst.GetSingleWordInOperand(0))) {
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
}
|
||||
break;
|
||||
case SpvOpTypeStruct: {
|
||||
// A struct type is basic if all of its members are basic.
|
||||
bool all_members_are_basic_types = true;
|
||||
for (uint32_t i = 0; i < inst.NumInOperands(); i++) {
|
||||
if (!basic_types.count(inst.GetSingleWordInOperand(i))) {
|
||||
all_members_are_basic_types = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_members_are_basic_types) {
|
||||
basic_types.insert(inst.result_id());
|
||||
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpTypePointer: {
|
||||
// We are interested in the pointer if its pointee type is basic and it
|
||||
// has the right storage class.
|
||||
auto pointee_type = inst.GetSingleWordInOperand(1);
|
||||
if (inst.GetSingleWordInOperand(0) == storage_class &&
|
||||
basic_types.count(pointee_type)) {
|
||||
// The pointer has the desired storage class, and its pointee type is
|
||||
// a basic type, so we are interested in it. Associate it with its
|
||||
// basic type.
|
||||
basic_type_to_pointers.at(pointee_type).push_back(inst.result_id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers};
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||
uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
|
||||
auto type_instruction =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
|
||||
assert(type_instruction && "The type instruction must exist.");
|
||||
switch (type_instruction->opcode()) {
|
||||
case SpvOpTypeBool:
|
||||
return FindOrCreateBoolConstant(false, is_irrelevant);
|
||||
case SpvOpTypeFloat: {
|
||||
auto width = type_instruction->GetSingleWordInOperand(0);
|
||||
auto num_words = (width + 32 - 1) / 32;
|
||||
return FindOrCreateFloatConstant(std::vector<uint32_t>(num_words, 0),
|
||||
width, is_irrelevant);
|
||||
}
|
||||
case SpvOpTypeInt: {
|
||||
auto width = type_instruction->GetSingleWordInOperand(0);
|
||||
auto num_words = (width + 32 - 1) / 32;
|
||||
return FindOrCreateIntegerConstant(
|
||||
std::vector<uint32_t>(num_words, 0), width,
|
||||
type_instruction->GetSingleWordInOperand(1), is_irrelevant);
|
||||
}
|
||||
case SpvOpTypeArray: {
|
||||
auto component_type_id = type_instruction->GetSingleWordInOperand(0);
|
||||
auto num_components =
|
||||
fuzzerutil::GetArraySize(*type_instruction, GetIRContext());
|
||||
return FindOrCreateCompositeConstant(
|
||||
std::vector<uint32_t>(
|
||||
num_components,
|
||||
FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector: {
|
||||
auto component_type_id = type_instruction->GetSingleWordInOperand(0);
|
||||
auto num_components = type_instruction->GetSingleWordInOperand(1);
|
||||
return FindOrCreateCompositeConstant(
|
||||
std::vector<uint32_t>(
|
||||
num_components,
|
||||
FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
case SpvOpTypeStruct: {
|
||||
std::vector<uint32_t> field_zero_ids;
|
||||
for (uint32_t index = 0; index < type_instruction->NumInOperands();
|
||||
index++) {
|
||||
field_zero_ids.push_back(FindOrCreateZeroConstant(
|
||||
type_instruction->GetSingleWordInOperand(index), is_irrelevant));
|
||||
}
|
||||
return FindOrCreateCompositeConstant(
|
||||
field_zero_ids, scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
default:
|
||||
assert(false && "Unknown type.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FuzzerPass::CanFindOrCreateZeroConstant(const opt::analysis::Type& type) {
|
||||
switch (type.kind()) {
|
||||
case opt::analysis::Type::kBool:
|
||||
case opt::analysis::Type::kInteger:
|
||||
case opt::analysis::Type::kFloat:
|
||||
case opt::analysis::Type::kArray:
|
||||
case opt::analysis::Type::kMatrix:
|
||||
case opt::analysis::Type::kVector:
|
||||
return true;
|
||||
case opt::analysis::Type::kStruct:
|
||||
return std::all_of(type.AsStruct()->element_types().begin(),
|
||||
type.AsStruct()->element_types().end(),
|
||||
[this](const opt::analysis::Type* element_type) {
|
||||
return CanFindOrCreateZeroConstant(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerPass::MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace) {
|
||||
// Only consider this use if it is in a block
|
||||
if (!GetIRContext()->get_instr_block(use_inst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the index of the operand restricted to input operands.
|
||||
uint32_t in_operand_index =
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
auto id_use_descriptor =
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
|
||||
uses_to_replace->emplace_back(
|
||||
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);
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateLocalVariable(
|
||||
uint32_t pointer_type_id, uint32_t function_id,
|
||||
bool pointee_value_is_irrelevant) {
|
||||
auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id);
|
||||
// No unused variables in release mode.
|
||||
(void)pointer_type;
|
||||
assert(pointer_type && pointer_type->AsPointer() &&
|
||||
pointer_type->AsPointer()->storage_class() ==
|
||||
SpvStorageClassFunction &&
|
||||
"The pointer_type_id must refer to a defined pointer type with "
|
||||
"storage class Function");
|
||||
auto function = fuzzerutil::FindFunction(GetIRContext(), function_id);
|
||||
assert(function && "The function must be defined.");
|
||||
|
||||
// First we try to find a suitable existing variable.
|
||||
// All of the local variable declarations are located in the first block.
|
||||
for (auto& instruction : *function->begin()) {
|
||||
if (instruction.opcode() != SpvOpVariable) {
|
||||
continue;
|
||||
}
|
||||
// The existing OpVariable must have type |pointer_type_id|.
|
||||
if (instruction.type_id() != pointer_type_id) {
|
||||
continue;
|
||||
}
|
||||
// Check if the found variable is marked with PointeeValueIsIrrelevant
|
||||
// according to |pointee_value_is_irrelevant|.
|
||||
if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
instruction.result_id()) != pointee_value_is_irrelevant) {
|
||||
continue;
|
||||
}
|
||||
return instruction.result_id();
|
||||
}
|
||||
|
||||
// No such variable was found. Apply a transformation to get one.
|
||||
uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
uint32_t result_id = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
result_id, pointer_type_id, function_id,
|
||||
FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant),
|
||||
pointee_value_is_irrelevant));
|
||||
return result_id;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateGlobalVariable(
|
||||
uint32_t pointer_type_id, bool pointee_value_is_irrelevant) {
|
||||
auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id);
|
||||
// No unused variables in release mode.
|
||||
(void)pointer_type;
|
||||
assert(
|
||||
pointer_type && pointer_type->AsPointer() &&
|
||||
(pointer_type->AsPointer()->storage_class() == SpvStorageClassPrivate ||
|
||||
pointer_type->AsPointer()->storage_class() ==
|
||||
SpvStorageClassWorkgroup) &&
|
||||
"The pointer_type_id must refer to a defined pointer type with storage "
|
||||
"class Private or Workgroup");
|
||||
|
||||
// First we try to find a suitable existing variable.
|
||||
for (auto& instruction : GetIRContext()->module()->types_values()) {
|
||||
if (instruction.opcode() != SpvOpVariable) {
|
||||
continue;
|
||||
}
|
||||
// The existing OpVariable must have type |pointer_type_id|.
|
||||
if (instruction.type_id() != pointer_type_id) {
|
||||
continue;
|
||||
}
|
||||
// Check if the found variable is marked with PointeeValueIsIrrelevant
|
||||
// according to |pointee_value_is_irrelevant|.
|
||||
if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
instruction.result_id()) != pointee_value_is_irrelevant) {
|
||||
continue;
|
||||
}
|
||||
return instruction.result_id();
|
||||
}
|
||||
|
||||
// No such variable was found. Apply a transformation to get one.
|
||||
uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
|
||||
GetIRContext(), pointer_type_id);
|
||||
uint32_t result_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// A variable with storage class Workgroup shouldn't have an initializer.
|
||||
if (storage_class == SpvStorageClassWorkgroup) {
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
result_id, pointer_type_id, SpvStorageClassWorkgroup, 0,
|
||||
pointee_value_is_irrelevant));
|
||||
} else {
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
result_id, pointer_type_id, SpvStorageClassPrivate,
|
||||
FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant),
|
||||
pointee_value_is_irrelevant));
|
||||
}
|
||||
return result_id;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
330
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
330
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@@ -1,330 +0,0 @@
|
||||
// Copyright (c) 2019 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_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_H_
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_context.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 {
|
||||
|
||||
// Interface for applying a pass of transformations to a module.
|
||||
class FuzzerPass {
|
||||
public:
|
||||
FuzzerPass(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
virtual ~FuzzerPass();
|
||||
|
||||
// Applies the pass to the module |ir_context_|, assuming and updating
|
||||
// information from |transformation_context_|, and using |fuzzer_context_| to
|
||||
// guide the process. Appends to |transformations_| all transformations that
|
||||
// were applied during the pass.
|
||||
virtual void Apply() = 0;
|
||||
|
||||
protected:
|
||||
opt::IRContext* GetIRContext() const { return ir_context_; }
|
||||
|
||||
TransformationContext* GetTransformationContext() const {
|
||||
return transformation_context_;
|
||||
}
|
||||
|
||||
FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; }
|
||||
|
||||
protobufs::TransformationSequence* GetTransformations() const {
|
||||
return transformations_;
|
||||
}
|
||||
|
||||
// Returns all instructions that are *available* at |inst_it|, which is
|
||||
// required to be inside block |block| of function |function| - that is, all
|
||||
// instructions at global scope and all instructions that strictly dominate
|
||||
// |inst_it|.
|
||||
//
|
||||
// Filters said instructions to return only those that satisfy the
|
||||
// |instruction_is_relevant| predicate. This, for instance, could ignore all
|
||||
// instructions that have a particular decoration.
|
||||
std::vector<opt::Instruction*> FindAvailableInstructions(
|
||||
opt::Function* function, opt::BasicBlock* block,
|
||||
const opt::BasicBlock::iterator& inst_it,
|
||||
std::function<bool(opt::IRContext*, opt::Instruction*)>
|
||||
instruction_is_relevant) const;
|
||||
|
||||
// A helper method that iterates through each instruction in each block, at
|
||||
// all times tracking an instruction descriptor that allows the latest
|
||||
// instruction to be located even if it has no result id.
|
||||
//
|
||||
// The code to manipulate the instruction descriptor is a bit fiddly. The
|
||||
// point of this method is to avoiding having to duplicate it in multiple
|
||||
// transformation passes.
|
||||
//
|
||||
// The function |action| is invoked for each instruction |inst_it| in block
|
||||
// |block| of function |function| that is encountered. The
|
||||
// |instruction_descriptor| parameter to the function object allows |inst_it|
|
||||
// to be identified.
|
||||
//
|
||||
// In most intended use cases, the job of |action| is to randomly decide
|
||||
// whether to try to apply some transformation, and then - if selected - to
|
||||
// attempt to apply it.
|
||||
void ForEachInstructionWithInstructionDescriptor(
|
||||
std::function<
|
||||
void(opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)>
|
||||
action);
|
||||
|
||||
// A generic helper for applying a transformation that should be applicable
|
||||
// by construction, and adding it to the sequence of applied transformations.
|
||||
void ApplyTransformation(const Transformation& transformation) {
|
||||
assert(transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
"Transformation should be applicable by construction.");
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
|
||||
// A generic helper for applying a transformation only if it is applicable.
|
||||
// If it is applicable, the transformation is applied and then added to the
|
||||
// sequence of applied transformations and the function returns true.
|
||||
// Otherwise, the function returns false.
|
||||
bool MaybeApplyTransformation(const Transformation& transformation) {
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the id of an OpTypeBool instruction. If such an instruction does
|
||||
// not exist, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateBoolType();
|
||||
|
||||
// Returns the id of an OpTypeInt instruction, with width and signedness
|
||||
// specified by |width| and |is_signed|, respectively. If such an instruction
|
||||
// does not exist, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateIntegerType(uint32_t width, bool is_signed);
|
||||
|
||||
// Returns the id of an OpTypeFloat instruction, with width specified by
|
||||
// |width|. If such an instruction does not exist, a transformation is
|
||||
// applied to add it.
|
||||
uint32_t FindOrCreateFloatType(uint32_t width);
|
||||
|
||||
// Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
|
||||
// instruction. If such an instruction doesn't exist, a transformation
|
||||
// is applied to create a new one.
|
||||
uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
|
||||
const std::vector<uint32_t>& argument_id);
|
||||
|
||||
// Returns the id of an OpTypeVector instruction, with |component_type_id|
|
||||
// (which must already exist) as its base type, and |component_count|
|
||||
// elements (which must be in the range [2, 4]). If such an instruction does
|
||||
// not exist, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateVectorType(uint32_t component_type_id,
|
||||
uint32_t component_count);
|
||||
|
||||
// Returns the id of an OpTypeMatrix instruction, with |column_count| columns
|
||||
// and |row_count| rows (each of which must be in the range [2, 4]). If the
|
||||
// float and vector types required to build this matrix type or the matrix
|
||||
// type itself do not exist, transformations are applied to add them.
|
||||
uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
|
||||
|
||||
// Returns the id of an OpTypeStruct instruction with |component_type_ids| as
|
||||
// type ids for struct's components. If no such a struct type exists,
|
||||
// transformations are applied to add it. |component_type_ids| may not contain
|
||||
// a result id of an OpTypeFunction.
|
||||
uint32_t FindOrCreateStructType(
|
||||
const std::vector<uint32_t>& component_type_ids);
|
||||
|
||||
// Returns the id of a pointer type with base type |base_type_id| (which must
|
||||
// already exist) and storage class |storage_class|. A transformation is
|
||||
// applied to add the pointer if it does not already exist.
|
||||
uint32_t FindOrCreatePointerType(uint32_t base_type_id,
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
// Returns the id of an OpTypePointer instruction, with a integer base
|
||||
// type of width and signedness specified by |width| and |is_signed|,
|
||||
// respectively. If the pointer type or required integer base type do not
|
||||
// exist, transformations are applied to add them.
|
||||
uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed,
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
// Returns the id of an OpConstant instruction, with a integer type of
|
||||
// width and signedness specified by |width| and |is_signed|, respectively,
|
||||
// with |words| as its value. If either the required integer type or the
|
||||
// constant do not exist, transformations are applied to add them.
|
||||
// The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t width, bool is_signed,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the id of an OpConstant instruction, with a floating-point
|
||||
// type of width specified by |width|, with |words| as its value. If either
|
||||
// the required floating-point type or the constant do not exist,
|
||||
// transformations are applied to add them. The returned id either
|
||||
// participates in IdIsIrrelevant fact or not, depending on the
|
||||
// |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateFloatConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t width, bool is_irrelevant);
|
||||
|
||||
// Returns the id of an OpConstantTrue or OpConstantFalse instruction,
|
||||
// according to |value|. If either the required instruction or the bool
|
||||
// type do not exist, transformations are applied to add them.
|
||||
// The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant);
|
||||
|
||||
// Returns the id of an OpConstant instruction of type with |type_id|
|
||||
// that consists of |words|. If that instruction doesn't exist,
|
||||
// transformations are applied to add it. |type_id| must be a valid
|
||||
// result id of either scalar or boolean OpType* instruction that exists
|
||||
// in the module. The returned id either participates in IdIsIrrelevant fact
|
||||
// or not, depending on the |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t type_id, bool is_irrelevant);
|
||||
|
||||
// Returns the id of an OpConstantComposite instruction of type with |type_id|
|
||||
// that consists of |component_ids|. If that instruction doesn't exist,
|
||||
// transformations are applied to add it. |type_id| must be a valid
|
||||
// result id of an OpType* instruction that represents a composite type
|
||||
// (i.e. a vector, matrix, struct or array).
|
||||
// The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateCompositeConstant(
|
||||
const std::vector<uint32_t>& component_ids, uint32_t type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the result id of an instruction of the form:
|
||||
// %id = OpUndef %|type_id|
|
||||
// If no such instruction exists, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
||||
|
||||
// Returns the id of an OpNullConstant instruction of type |type_id|. If
|
||||
// that instruction doesn't exist, it is added through a transformation.
|
||||
// |type_id| must be a valid result id of an OpType* instruction that exists
|
||||
// in the module.
|
||||
uint32_t FindOrCreateNullConstant(uint32_t type_id);
|
||||
|
||||
// Define a *basic type* to be an integer, boolean or floating-point type,
|
||||
// or a matrix, vector, struct or fixed-size array built from basic types. In
|
||||
// particular, a basic type cannot contain an opaque type (such as an image),
|
||||
// or a runtime-sized array.
|
||||
//
|
||||
// Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that:
|
||||
// - basic_type_ids captures every basic type declared in the module.
|
||||
// - basic_type_ids_to_pointers maps every such basic type to the sequence
|
||||
// of all pointer types that have storage class |storage_class| and the
|
||||
// given basic type as their pointee type. The sequence may be empty for
|
||||
// some basic types if no pointers to those types are defined for the given
|
||||
// storage class, and the sequence will have multiple elements if there are
|
||||
// repeated pointer declarations for the same basic type and storage class.
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const;
|
||||
|
||||
// Given a type id, |scalar_or_composite_type_id|, which must correspond to
|
||||
// some scalar or composite type, returns the result id of an instruction
|
||||
// defining a constant of the given type that is zero or false at everywhere.
|
||||
// If such an instruction does not yet exist, transformations are applied to
|
||||
// add it. The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
//
|
||||
// Examples:
|
||||
// --------------+-------------------------------
|
||||
// TYPE | RESULT is id corresponding to
|
||||
// --------------+-------------------------------
|
||||
// bool | false
|
||||
// --------------+-------------------------------
|
||||
// bvec4 | (false, false, false, false)
|
||||
// --------------+-------------------------------
|
||||
// float | 0.0
|
||||
// --------------+-------------------------------
|
||||
// vec2 | (0.0, 0.0)
|
||||
// --------------+-------------------------------
|
||||
// int[3] | [0, 0, 0]
|
||||
// --------------+-------------------------------
|
||||
// struct S { |
|
||||
// int i; | S(0, false, (0u, 0u))
|
||||
// bool b; |
|
||||
// uint2 u; |
|
||||
// } |
|
||||
// --------------+-------------------------------
|
||||
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Checks if FindOrCreateZeroConstant can be called on this type.
|
||||
bool CanFindOrCreateZeroConstant(const opt::analysis::Type& type);
|
||||
|
||||
// Adds a pair (id_use_descriptor, |replacement_id|) to the vector
|
||||
// |uses_to_replace|, where id_use_descriptor is the id use descriptor
|
||||
// representing the usage of an id in the |use_inst| instruction, at operand
|
||||
// index |use_index|, only if the instruction is in a basic block.
|
||||
// If the instruction is not in a basic block, it does nothing.
|
||||
void MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
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);
|
||||
|
||||
// Returns the id of an available local variable (storage class Function) with
|
||||
// the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. If there is no such variable, it creates one
|
||||
// in the |function| adding a zero initializer constant that is irrelevant.
|
||||
// The new variable has the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. The function returns the id of the created
|
||||
// variable.
|
||||
uint32_t FindOrCreateLocalVariable(uint32_t pointer_type_id,
|
||||
uint32_t function_id,
|
||||
bool pointee_value_is_irrelevant);
|
||||
|
||||
// Returns the id of an available global variable (storage class Private or
|
||||
// Workgroup) with the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. If there is no such variable, it creates
|
||||
// one, adding a zero initializer constant that is irrelevant. The new
|
||||
// variable has the fact PointeeValueIsIrrelevant set according to
|
||||
// |pointee_value_is_irrelevant|. The function returns the id of the created
|
||||
// variable.
|
||||
uint32_t FindOrCreateGlobalVariable(uint32_t pointer_type_id,
|
||||
bool pointee_value_is_irrelevant);
|
||||
|
||||
private:
|
||||
opt::IRContext* ir_context_;
|
||||
TransformationContext* transformation_context_;
|
||||
FuzzerContext* fuzzer_context_;
|
||||
protobufs::TransformationSequence* transformations_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_H_
|
||||
@@ -1,192 +0,0 @@
|
||||
// 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_access_chains.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_access_chain.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
|
||||
|
||||
void FuzzerPassAddAccessChains::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(inst_it->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Check whether it is legitimate to insert an access chain
|
||||
// instruction before this instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain,
|
||||
inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting a load here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingAccessChain())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all of the pointers that are currently in scope, excluding
|
||||
// explicitly null and undefined pointers.
|
||||
std::vector<opt::Instruction*> relevant_pointer_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[](opt::IRContext* context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
// A pointer needs both a result and type id.
|
||||
return false;
|
||||
}
|
||||
switch (instruction->opcode()) {
|
||||
case SpvOpConstantNull:
|
||||
case SpvOpUndef:
|
||||
// Do not allow making an access chain from a null or
|
||||
// undefined pointer. (We can eliminate these cases
|
||||
// before actually checking that the instruction is a
|
||||
// pointer.)
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// If the instruction has pointer type, we can legitimately
|
||||
// make an access chain from it.
|
||||
return context->get_def_use_mgr()
|
||||
->GetDef(instruction->type_id())
|
||||
->opcode() == SpvOpTypePointer;
|
||||
});
|
||||
|
||||
// At this point, |relevant_instructions| contains all the pointers
|
||||
// we might think of making an access chain from.
|
||||
if (relevant_pointer_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto chosen_pointer =
|
||||
relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
|
||||
relevant_pointer_instructions)];
|
||||
std::vector<uint32_t> index_ids;
|
||||
|
||||
// Each index accessing a non-struct composite will be clamped, thus
|
||||
// needing a pair of fresh ids
|
||||
std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
|
||||
|
||||
auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
chosen_pointer->type_id());
|
||||
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
|
||||
while (true) {
|
||||
auto subobject_type =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id);
|
||||
if (!spvOpcodeIsComposite(subobject_type->opcode())) {
|
||||
break;
|
||||
}
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfGoingDeeperWhenMakingAccessChain())) {
|
||||
break;
|
||||
}
|
||||
uint32_t bound;
|
||||
switch (subobject_type->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
bound = subobject_type->GetSingleWordInOperand(1);
|
||||
break;
|
||||
case SpvOpTypeStruct:
|
||||
bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Not a composite type opcode.");
|
||||
// Set the bound to a value in order to keep release compilers
|
||||
// happy.
|
||||
bound = 0;
|
||||
break;
|
||||
}
|
||||
if (bound == 0) {
|
||||
// It is possible for a composite type to legitimately have zero
|
||||
// sub-components, at least in the case of a struct, which
|
||||
// can have no fields.
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t index_value =
|
||||
GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
|
||||
|
||||
switch (subobject_type->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector: {
|
||||
// The index will be clamped
|
||||
|
||||
bool is_signed = GetFuzzerContext()->ChooseEven();
|
||||
|
||||
// Make the constant ready for clamping. We need:
|
||||
// - an OpTypeBool to be present in the module
|
||||
// - an OpConstant with the same type as the index and value
|
||||
// the maximum value for an index
|
||||
// - a new pair of fresh ids for the clamping instructions
|
||||
FindOrCreateBoolType();
|
||||
FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
|
||||
std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
|
||||
GetFuzzerContext()->GetFreshId(),
|
||||
GetFuzzerContext()->GetFreshId()};
|
||||
fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
|
||||
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, is_signed, false));
|
||||
subobject_type_id = subobject_type->GetSingleWordInOperand(0);
|
||||
|
||||
} break;
|
||||
case SpvOpTypeStruct:
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
|
||||
subobject_type_id =
|
||||
subobject_type->GetSingleWordInOperand(index_value);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Not a composite type opcode.");
|
||||
}
|
||||
}
|
||||
// The transformation we are about to create will only apply if a
|
||||
// pointer suitable for the access chain's result type exists, so we
|
||||
// create one if it does not.
|
||||
FindOrCreatePointerType(subobject_type_id,
|
||||
static_cast<SpvStorageClass>(
|
||||
pointer_type->GetSingleWordInOperand(0)));
|
||||
// Apply the transformation to add an access chain.
|
||||
ApplyTransformation(TransformationAccessChain(
|
||||
GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
|
||||
index_ids, instruction_descriptor, fresh_ids_for_clamping));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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_ACCESS_CHAINS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that randomly adds access chains based on pointers available in
|
||||
// the module. Other passes can use these access chains, e.g. by loading from
|
||||
// them.
|
||||
class FuzzerPassAddAccessChains : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddAccessChains(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddAccessChains();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
|
||||
@@ -1,236 +0,0 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_add_composite_inserts.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
#include "source/fuzz/transformation_composite_insert.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddCompositeInserts::~FuzzerPassAddCompositeInserts() = default;
|
||||
|
||||
void FuzzerPassAddCompositeInserts::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator instruction_iterator,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(instruction_iterator->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Randomly decide whether to try adding an OpCompositeInsert
|
||||
// instruction.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It must be possible to insert an OpCompositeInsert instruction
|
||||
// before |instruction_iterator|.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
|
||||
SpvOpCompositeInsert, instruction_iterator)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for available values that have composite type.
|
||||
std::vector<opt::Instruction*> available_composites =
|
||||
FindAvailableInstructions(
|
||||
function, block, instruction_iterator,
|
||||
[instruction_descriptor](
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
// |instruction| must be a supported instruction of composite
|
||||
// type.
|
||||
if (!TransformationCompositeInsert::
|
||||
IsCompositeInstructionSupported(ir_context,
|
||||
instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto instruction_type = ir_context->get_type_mgr()->GetType(
|
||||
instruction->type_id());
|
||||
|
||||
// No components of the composite can have type
|
||||
// OpTypeRuntimeArray.
|
||||
if (ContainsRuntimeArray(*instruction_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No components of the composite can be pointers.
|
||||
// TODO:
|
||||
// (https://github.com/KhronosGroup/SPIRV-Tools/issues/3658)
|
||||
// Structs can have components of pointer type.
|
||||
// FindOrCreateZeroConstant cannot be called on a
|
||||
// pointer. We ignore pointers for now. Consider adding
|
||||
// support for pointer types.
|
||||
if (ContainsPointer(*instruction_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// If there are no available values, then return.
|
||||
if (available_composites.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose randomly one available composite value.
|
||||
auto available_composite =
|
||||
available_composites[GetFuzzerContext()->RandomIndex(
|
||||
available_composites)];
|
||||
|
||||
// Take a random component of the chosen composite value. If the chosen
|
||||
// component is itself a composite, then randomly decide whether to take
|
||||
// its component and repeat.
|
||||
uint32_t current_node_type_id = available_composite->type_id();
|
||||
std::vector<uint32_t> path_to_replaced;
|
||||
while (true) {
|
||||
auto current_node_type_inst =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id);
|
||||
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*current_node_type_inst, GetIRContext());
|
||||
|
||||
// If the composite is empty, then end the iteration.
|
||||
if (num_of_components == 0) {
|
||||
break;
|
||||
}
|
||||
uint32_t one_selected_index =
|
||||
GetFuzzerContext()->GetRandomIndexForCompositeInsert(
|
||||
num_of_components);
|
||||
|
||||
// Construct a final index by appending the current index.
|
||||
path_to_replaced.push_back(one_selected_index);
|
||||
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
|
||||
GetIRContext(), current_node_type_id, one_selected_index);
|
||||
|
||||
// If the component is not a composite then end the iteration.
|
||||
if (!fuzzerutil::IsCompositeType(
|
||||
GetIRContext()->get_type_mgr()->GetType(
|
||||
current_node_type_id))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the component is a composite, but we decide not to go deeper,
|
||||
// then end the iteration.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfGoingDeeperToInsertInComposite())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for available objects that have the type id
|
||||
// |current_node_type_id| and can be inserted.
|
||||
std::vector<opt::Instruction*> available_objects =
|
||||
FindAvailableInstructions(
|
||||
function, block, instruction_iterator,
|
||||
[instruction_descriptor, current_node_type_id](
|
||||
opt::IRContext* /*unused*/,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (instruction->result_id() == 0 ||
|
||||
instruction->type_id() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (instruction->type_id() != current_node_type_id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// If there are no objects of the specific type available, check if
|
||||
// FindOrCreateZeroConstant can be called and create a zero constant of
|
||||
// this type.
|
||||
uint32_t available_object_id;
|
||||
if (available_objects.empty()) {
|
||||
auto current_node_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(current_node_type_id);
|
||||
if (!CanFindOrCreateZeroConstant(*current_node_type)) {
|
||||
return;
|
||||
}
|
||||
available_object_id =
|
||||
FindOrCreateZeroConstant(current_node_type_id, false);
|
||||
} else {
|
||||
available_object_id =
|
||||
available_objects[GetFuzzerContext()->RandomIndex(
|
||||
available_objects)]
|
||||
->result_id();
|
||||
}
|
||||
auto new_result_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// Insert an OpCompositeInsert instruction which copies
|
||||
// |available_composite| and in the copy inserts the object
|
||||
// of type |available_object_id| at index |index_to_replace|.
|
||||
ApplyTransformation(TransformationCompositeInsert(
|
||||
instruction_descriptor, new_result_id,
|
||||
available_composite->result_id(), available_object_id,
|
||||
path_to_replaced));
|
||||
});
|
||||
}
|
||||
|
||||
bool FuzzerPassAddCompositeInserts::ContainsPointer(
|
||||
const opt::analysis::Type& type) {
|
||||
switch (type.kind()) {
|
||||
case opt::analysis::Type::kPointer:
|
||||
return true;
|
||||
case opt::analysis::Type::kArray:
|
||||
return ContainsPointer(*type.AsArray()->element_type());
|
||||
case opt::analysis::Type::kMatrix:
|
||||
return ContainsPointer(*type.AsMatrix()->element_type());
|
||||
case opt::analysis::Type::kVector:
|
||||
return ContainsPointer(*type.AsVector()->element_type());
|
||||
case opt::analysis::Type::kStruct:
|
||||
return std::any_of(type.AsStruct()->element_types().begin(),
|
||||
type.AsStruct()->element_types().end(),
|
||||
[](const opt::analysis::Type* element_type) {
|
||||
return ContainsPointer(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray(
|
||||
const opt::analysis::Type& type) {
|
||||
switch (type.kind()) {
|
||||
case opt::analysis::Type::kRuntimeArray:
|
||||
return true;
|
||||
case opt::analysis::Type::kStruct:
|
||||
// If any component of a struct is of type OpTypeRuntimeArray, return
|
||||
// true.
|
||||
return std::any_of(type.AsStruct()->element_types().begin(),
|
||||
type.AsStruct()->element_types().end(),
|
||||
[](const opt::analysis::Type* element_type) {
|
||||
return ContainsRuntimeArray(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that randomly adds new OpCompositeInsert instructions to
|
||||
// available values that have the composite type.
|
||||
class FuzzerPassAddCompositeInserts : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddCompositeInserts(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddCompositeInserts();
|
||||
void Apply() override;
|
||||
|
||||
// Checks if any component of a composite is a pointer.
|
||||
static bool ContainsPointer(const opt::analysis::Type& type);
|
||||
|
||||
// Checks if any component of a composite has type OpTypeRuntimeArray.
|
||||
static bool ContainsRuntimeArray(const opt::analysis::Type& type);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_type_array.h"
|
||||
#include "source/fuzz/transformation_add_type_struct.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default;
|
||||
|
||||
void FuzzerPassAddCompositeTypes::Apply() {
|
||||
MaybeAddMissingVectorTypes();
|
||||
MaybeAddMissingMatrixTypes();
|
||||
|
||||
// Randomly interleave between adding struct and array composite types
|
||||
while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) {
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) {
|
||||
AddNewStructType();
|
||||
} else {
|
||||
AddNewArrayType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() {
|
||||
// Functions to lazily supply scalar base types on demand if we decide to
|
||||
// create vectors with the relevant base types.
|
||||
std::function<uint32_t()> bool_type_supplier = [this]() -> uint32_t {
|
||||
return FindOrCreateBoolType();
|
||||
};
|
||||
std::function<uint32_t()> float_type_supplier = [this]() -> uint32_t {
|
||||
return FindOrCreateFloatType(32);
|
||||
};
|
||||
std::function<uint32_t()> int_type_supplier = [this]() -> uint32_t {
|
||||
return FindOrCreateIntegerType(32, true);
|
||||
};
|
||||
std::function<uint32_t()> uint_type_supplier = [this]() -> uint32_t {
|
||||
return FindOrCreateIntegerType(32, false);
|
||||
};
|
||||
|
||||
// Consider each of the base types with which we can make vectors.
|
||||
for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier,
|
||||
int_type_supplier, uint_type_supplier}) {
|
||||
// Consider each valid vector size.
|
||||
for (uint32_t size = 2; size <= 4; size++) {
|
||||
// Randomly decide whether to create (if it does not already exist) a
|
||||
// vector with this size and base type.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingVectorType())) {
|
||||
FindOrCreateVectorType(base_type_supplier(), size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() {
|
||||
// Consider every valid matrix dimension.
|
||||
for (uint32_t columns = 2; columns <= 4; columns++) {
|
||||
for (uint32_t rows = 2; rows <= 4; rows++) {
|
||||
// Randomly decide whether to create (if it does not already exist) a
|
||||
// matrix with these dimensions. As matrices can only have floating-point
|
||||
// base type, we do not need to consider multiple base types as in the
|
||||
// case for vectors.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingMatrixType())) {
|
||||
FindOrCreateMatrixType(columns, rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerPassAddCompositeTypes::AddNewArrayType() {
|
||||
ApplyTransformation(TransformationAddTypeArray(
|
||||
GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
|
||||
FindOrCreateIntegerConstant(
|
||||
{GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false)));
|
||||
}
|
||||
|
||||
void FuzzerPassAddCompositeTypes::AddNewStructType() {
|
||||
std::vector<uint32_t> field_type_ids;
|
||||
do {
|
||||
field_type_ids.push_back(ChooseScalarOrCompositeType());
|
||||
} while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingAnotherStructField()));
|
||||
ApplyTransformation(TransformationAddTypeStruct(
|
||||
GetFuzzerContext()->GetFreshId(), field_type_ids));
|
||||
}
|
||||
|
||||
uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() {
|
||||
// Gather up all the possibly-relevant types.
|
||||
std::vector<uint32_t> candidates;
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeStruct:
|
||||
case SpvOpTypeVector:
|
||||
candidates.push_back(inst.result_id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(!candidates.empty() &&
|
||||
"This function should only be called if there is at least one scalar "
|
||||
"or composite type available.");
|
||||
// Return one of these types at random.
|
||||
return candidates[GetFuzzerContext()->RandomIndex(candidates)];
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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_COMPOSITE_TYPES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that randomly adds missing vector and matrix types, and new
|
||||
// array and struct types, to the module.
|
||||
class FuzzerPassAddCompositeTypes : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddCompositeTypes(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddCompositeTypes();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Creates an array of a random size with a random existing base type and adds
|
||||
// it to the module.
|
||||
void AddNewArrayType();
|
||||
|
||||
// Creates a struct with fields of random existing types and adds it to the
|
||||
// module.
|
||||
void AddNewStructType();
|
||||
|
||||
// For each vector type not already present in the module, randomly decides
|
||||
// whether to add it to the module.
|
||||
void MaybeAddMissingVectorTypes();
|
||||
|
||||
// For each matrix type not already present in the module, randomly decides
|
||||
// whether to add it to the module.
|
||||
void MaybeAddMissingMatrixTypes();
|
||||
|
||||
// Returns the id of a scalar or composite type declared in the module,
|
||||
// chosen randomly.
|
||||
uint32_t ChooseScalarOrCompositeType();
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_copy_memory.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default;
|
||||
|
||||
void FuzzerPassAddCopyMemory::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
// Check that we can insert an OpCopyMemory before this instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory,
|
||||
inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingCopyMemory())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all instructions available before |inst_it| according to the
|
||||
// domination rules.
|
||||
auto instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
TransformationAddCopyMemory::IsInstructionSupported);
|
||||
|
||||
if (instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* inst =
|
||||
instructions[GetFuzzerContext()->RandomIndex(instructions)];
|
||||
|
||||
// Decide whether to create global or local variable.
|
||||
auto storage_class = GetFuzzerContext()->ChooseEven()
|
||||
? SpvStorageClassPrivate
|
||||
: SpvStorageClassFunction;
|
||||
|
||||
auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), inst->type_id());
|
||||
|
||||
// Create a pointer type with |storage_class| if needed.
|
||||
FindOrCreatePointerType(pointee_type_id, storage_class);
|
||||
|
||||
ApplyTransformation(TransformationAddCopyMemory(
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId(),
|
||||
inst->result_id(), storage_class,
|
||||
FindOrCreateZeroConstant(pointee_type_id, false)));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Randomly decides whether to add OpCopyMemory before some instruction in the
|
||||
// module.
|
||||
class FuzzerPassAddCopyMemory : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddCopyMemory(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddCopyMemory() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright (c) 2019 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_dead_blocks.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_dead_block.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
|
||||
|
||||
void FuzzerPassAddDeadBlocks::Apply() {
|
||||
// We iterate over all blocks in the module collecting up those at which we
|
||||
// might add a branch to a new dead block. We then loop over all such
|
||||
// candidates and actually apply transformations. This separation is to
|
||||
// avoid modifying the module as we traverse it.
|
||||
std::vector<TransformationAddDeadBlock> candidate_transformations;
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadBlock())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the module contains a boolean constant equal to
|
||||
// |condition_value|.
|
||||
bool condition_value = GetFuzzerContext()->ChooseEven();
|
||||
FindOrCreateBoolConstant(condition_value, false);
|
||||
|
||||
// We speculatively create a transformation, and then apply it (below) if
|
||||
// it turns out to be applicable. This avoids duplicating the logic for
|
||||
// applicability checking.
|
||||
//
|
||||
// It means that fresh ids for transformations that turn out not to be
|
||||
// applicable end up being unused.
|
||||
candidate_transformations.emplace_back(TransformationAddDeadBlock(
|
||||
GetFuzzerContext()->GetFreshId(), block.id(), condition_value));
|
||||
}
|
||||
}
|
||||
// Apply all those transformations that are in fact applicable.
|
||||
for (auto& transformation : candidate_transformations) {
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2019 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_DEAD_BLOCKS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass to add dynamically unreachable blocks to the module. Future
|
||||
// passes can then manipulate such blocks.
|
||||
class FuzzerPassAddDeadBlocks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadBlocks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddDeadBlocks();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright (c) 2019 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_dead_breaks.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_dead_break.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default;
|
||||
|
||||
void FuzzerPassAddDeadBreaks::Apply() {
|
||||
// We first collect up lots of possibly-applicable transformations.
|
||||
std::vector<TransformationAddDeadBreak> candidate_transformations;
|
||||
// We consider each function separately.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// For a given function, we find all the merge blocks in that function.
|
||||
std::vector<opt::BasicBlock*> merge_blocks;
|
||||
for (auto& block : function) {
|
||||
auto maybe_merge_id = block.MergeBlockIdIfAny();
|
||||
if (maybe_merge_id) {
|
||||
auto merge_block =
|
||||
fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id);
|
||||
|
||||
assert(merge_block && "Merge block can't be null");
|
||||
|
||||
merge_blocks.push_back(merge_block);
|
||||
}
|
||||
}
|
||||
// We rather aggressively consider the possibility of adding a break from
|
||||
// every block in the function to every merge block. Many of these will be
|
||||
// inapplicable as they would be illegal. That's OK - we later discard the
|
||||
// ones that turn out to be no good.
|
||||
for (auto& block : function) {
|
||||
for (auto* merge_block : merge_blocks) {
|
||||
// Populate this vector with ids that are available at the branch point
|
||||
// of this basic block. We will use these ids to update OpPhi
|
||||
// instructions later.
|
||||
std::vector<uint32_t> phi_ids;
|
||||
|
||||
// Determine how we need to adjust OpPhi instructions' operands
|
||||
// for this transformation to be valid.
|
||||
//
|
||||
// If |block| has a branch to |merge_block|, the latter must have all of
|
||||
// its OpPhi instructions set up correctly - we don't need to adjust
|
||||
// anything.
|
||||
if (!block.IsSuccessor(merge_block)) {
|
||||
merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
|
||||
// Add an additional operand for OpPhi instruction.
|
||||
//
|
||||
// We mark the constant as irrelevant so that we can replace it with
|
||||
// a more interesting value later.
|
||||
phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure the module has a required boolean constant to be used in
|
||||
// OpBranchConditional instruction.
|
||||
auto break_condition = GetFuzzerContext()->ChooseEven();
|
||||
FindOrCreateBoolConstant(break_condition, false);
|
||||
|
||||
auto candidate_transformation = TransformationAddDeadBreak(
|
||||
block.id(), merge_block->id(), break_condition, std::move(phi_ids));
|
||||
if (candidate_transformation.IsApplicable(
|
||||
GetIRContext(), *GetTransformationContext())) {
|
||||
// Only consider a transformation as a candidate if it is applicable.
|
||||
candidate_transformations.push_back(
|
||||
std::move(candidate_transformation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the candidate transformations that were accumulated,
|
||||
// probabilistically deciding whether to consider each one further and
|
||||
// applying the still-applicable ones that are considered further.
|
||||
//
|
||||
// We iterate through the candidate transformations in a random order by
|
||||
// repeatedly removing a random candidate transformation from the sequence
|
||||
// until no candidate transformations remain. This is done because
|
||||
// transformations can potentially disable one another, so that iterating
|
||||
// through them in order would lead to a higher probability of
|
||||
// transformations appearing early in the sequence being applied compared
|
||||
// with later transformations.
|
||||
while (!candidate_transformations.empty()) {
|
||||
// Choose a random index into the sequence of remaining candidate
|
||||
// transformations.
|
||||
auto index = GetFuzzerContext()->RandomIndex(candidate_transformations);
|
||||
// Remove the transformation at the chosen index from the sequence.
|
||||
auto transformation = std::move(candidate_transformations[index]);
|
||||
candidate_transformations.erase(candidate_transformations.begin() + index);
|
||||
// Probabilistically decide whether to try to apply it vs. ignore it, in the
|
||||
// case that it is applicable.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_DEAD_BREAKS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass for adding dead break edges to the module.
|
||||
class FuzzerPassAddDeadBreaks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadBreaks(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddDeadBreaks();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright (c) 2019 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_dead_continues.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_dead_continue.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default;
|
||||
|
||||
void FuzzerPassAddDeadContinues::Apply() {
|
||||
// Consider every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// Get the label id of the continue target of the innermost loop.
|
||||
auto continue_block_id =
|
||||
block.IsLoopHeader()
|
||||
? block.ContinueBlockId()
|
||||
: GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock(
|
||||
block.id());
|
||||
|
||||
// This transformation is not applicable if current block is not inside a
|
||||
// loop.
|
||||
if (continue_block_id == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* continue_block =
|
||||
fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id);
|
||||
assert(continue_block && "Continue block is null");
|
||||
|
||||
// Analyze return type of each OpPhi instruction in the continue target
|
||||
// and provide an id for the transformation if needed.
|
||||
std::vector<uint32_t> phi_ids;
|
||||
// Check whether current block has an edge to the continue target.
|
||||
// If this is the case, we don't need to do anything.
|
||||
if (!block.IsSuccessor(continue_block)) {
|
||||
continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
|
||||
// Add an additional operand for OpPhi instruction.
|
||||
//
|
||||
// We mark the constant as irrelevant so that we can replace it with a
|
||||
// more interesting value later.
|
||||
phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure the module contains a boolean constant equal to
|
||||
// |condition_value|.
|
||||
bool condition_value = GetFuzzerContext()->ChooseEven();
|
||||
FindOrCreateBoolConstant(condition_value, false);
|
||||
|
||||
// Make a transformation to add a dead continue from this node; if the
|
||||
// node turns out to be inappropriate (e.g. by not being in a loop) the
|
||||
// precondition for the transformation will fail and it will be ignored.
|
||||
auto candidate_transformation = TransformationAddDeadContinue(
|
||||
block.id(), condition_value, std::move(phi_ids));
|
||||
// Probabilistically decide whether to apply the transformation in the
|
||||
// case that it is applicable.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
|
||||
MaybeApplyTransformation(candidate_transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_DEAD_CONTINUES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass for adding dead continue edges to the module.
|
||||
class FuzzerPassAddDeadContinues : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddDeadContinues(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddDeadContinues();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_
|
||||
@@ -1,413 +0,0 @@
|
||||
// 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_equation_instructions.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_equation_instruction.h"
|
||||
|
||||
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,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
|
||||
default;
|
||||
|
||||
void FuzzerPassAddEquationInstructions::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that it is OK to add an equation instruction before the given
|
||||
// instruction in principle - e.g. check that this does not lead to
|
||||
// inserting before an OpVariable or OpPhi instruction. We use OpIAdd
|
||||
// as an example opcode for this check, to be representative of *some*
|
||||
// opcode that defines an equation, even though we may choose a
|
||||
// different opcode below.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all available instructions with result ids and types that are not
|
||||
// OpUndef.
|
||||
std::vector<opt::Instruction*> available_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this](opt::IRContext*, opt::Instruction* instruction) -> bool {
|
||||
return instruction->result_id() && instruction->type_id() &&
|
||||
instruction->opcode() != SpvOpUndef &&
|
||||
!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->IdIsIrrelevant(instruction->result_id());
|
||||
});
|
||||
|
||||
// Try the opcodes for which we know how to make ids at random until
|
||||
// something works.
|
||||
std::vector<SpvOp> candidate_opcodes = {
|
||||
SpvOpIAdd, SpvOpISub, SpvOpLogicalNot, SpvOpSNegate,
|
||||
SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
|
||||
do {
|
||||
auto opcode =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
|
||||
switch (opcode) {
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF: {
|
||||
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;
|
||||
}
|
||||
|
||||
const auto* operand =
|
||||
candidate_instructions[GetFuzzerContext()->RandomIndex(
|
||||
candidate_instructions)];
|
||||
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(operand->type_id());
|
||||
assert(type && "Operand has invalid type");
|
||||
|
||||
// Make sure a result type exists in the module.
|
||||
if (const auto* vector = type->AsVector()) {
|
||||
// We store element count in a separate variable since the
|
||||
// call FindOrCreate* functions below might invalidate
|
||||
// |vector| pointer.
|
||||
const auto element_count = vector->element_count();
|
||||
|
||||
FindOrCreateVectorType(
|
||||
FindOrCreateFloatType(
|
||||
vector->element_type()->AsInteger()->width()),
|
||||
element_count);
|
||||
} else {
|
||||
FindOrCreateFloatType(type->AsInteger()->width());
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{operand->result_id()}, instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
case SpvOpBitcast: {
|
||||
const auto candidate_instructions =
|
||||
GetNumericalInstructions(available_instructions);
|
||||
|
||||
if (!candidate_instructions.empty()) {
|
||||
const auto* operand_inst =
|
||||
candidate_instructions[GetFuzzerContext()->RandomIndex(
|
||||
candidate_instructions)];
|
||||
const auto* operand_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(
|
||||
operand_inst->type_id());
|
||||
assert(operand_type && "Operand instruction has invalid type");
|
||||
|
||||
// Make sure a result type exists in the module.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
|
||||
// The only constraint on the types of OpBitcast's parameters
|
||||
// is that they must have the same number of bits. Consider
|
||||
// improving the code below to support this in full.
|
||||
if (const auto* vector = operand_type->AsVector()) {
|
||||
// We store element count in a separate variable since the
|
||||
// call FindOrCreate* functions below might invalidate
|
||||
// |vector| pointer.
|
||||
const auto element_count = vector->element_count();
|
||||
|
||||
uint32_t element_type_id;
|
||||
if (const auto* int_type =
|
||||
vector->element_type()->AsInteger()) {
|
||||
element_type_id = FindOrCreateFloatType(int_type->width());
|
||||
} else {
|
||||
assert(vector->element_type()->AsFloat() &&
|
||||
"Vector must have numerical elements");
|
||||
element_type_id = FindOrCreateIntegerType(
|
||||
vector->element_type()->AsFloat()->width(),
|
||||
GetFuzzerContext()->ChooseEven());
|
||||
}
|
||||
|
||||
FindOrCreateVectorType(element_type_id, element_count);
|
||||
} else if (const auto* int_type = operand_type->AsInteger()) {
|
||||
FindOrCreateFloatType(int_type->width());
|
||||
} else {
|
||||
assert(operand_type->AsFloat() &&
|
||||
"Operand is not a scalar of numerical type");
|
||||
FindOrCreateIntegerType(operand_type->AsFloat()->width(),
|
||||
GetFuzzerContext()->ChooseEven());
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{operand_inst->result_id()}, instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub: {
|
||||
// Instructions of integer (scalar or vector) result type are
|
||||
// suitable for these opcodes.
|
||||
auto integer_instructions =
|
||||
GetIntegerInstructions(available_instructions);
|
||||
if (!integer_instructions.empty()) {
|
||||
// There is at least one such instruction, so pick one at random
|
||||
// for the LHS of an equation.
|
||||
auto lhs = integer_instructions.at(
|
||||
GetFuzzerContext()->RandomIndex(integer_instructions));
|
||||
|
||||
// For the RHS, we can use any instruction with an integer
|
||||
// scalar/vector result type of the same number of components
|
||||
// and the same bit-width for the underlying integer type.
|
||||
|
||||
// Work out the element count and bit-width.
|
||||
auto lhs_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(lhs->type_id());
|
||||
uint32_t lhs_element_count;
|
||||
uint32_t lhs_bit_width;
|
||||
if (lhs_type->AsVector()) {
|
||||
lhs_element_count = lhs_type->AsVector()->element_count();
|
||||
lhs_bit_width = lhs_type->AsVector()
|
||||
->element_type()
|
||||
->AsInteger()
|
||||
->width();
|
||||
} else {
|
||||
lhs_element_count = 1;
|
||||
lhs_bit_width = lhs_type->AsInteger()->width();
|
||||
}
|
||||
|
||||
// Get all the instructions that match on element count and
|
||||
// bit-width.
|
||||
auto candidate_rhs_instructions = RestrictToElementBitWidth(
|
||||
RestrictToVectorWidth(integer_instructions,
|
||||
lhs_element_count),
|
||||
lhs_bit_width);
|
||||
|
||||
// Choose a RHS instruction at random; there is guaranteed to
|
||||
// be at least one choice as the LHS will be available.
|
||||
auto rhs = candidate_rhs_instructions.at(
|
||||
GetFuzzerContext()->RandomIndex(
|
||||
candidate_rhs_instructions));
|
||||
|
||||
// Add the equation instruction.
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{lhs->result_id(), rhs->result_id()},
|
||||
instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpLogicalNot: {
|
||||
// Choose any available instruction of boolean scalar/vector
|
||||
// result type and equate its negation with a fresh id.
|
||||
auto boolean_instructions =
|
||||
GetBooleanInstructions(available_instructions);
|
||||
if (!boolean_instructions.empty()) {
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{boolean_instructions
|
||||
.at(GetFuzzerContext()->RandomIndex(
|
||||
boolean_instructions))
|
||||
->result_id()},
|
||||
instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpvOpSNegate: {
|
||||
// Similar to OpLogicalNot, but for signed integer negation.
|
||||
auto integer_instructions =
|
||||
GetIntegerInstructions(available_instructions);
|
||||
if (!integer_instructions.empty()) {
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{integer_instructions
|
||||
.at(GetFuzzerContext()->RandomIndex(
|
||||
integer_instructions))
|
||||
->result_id()},
|
||||
instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unexpected opcode.");
|
||||
break;
|
||||
}
|
||||
} while (!candidate_opcodes.empty());
|
||||
// Reaching here means that we did not manage to apply any
|
||||
// transformation at this point of the module.
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetIntegerInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
if (type->AsInteger() ||
|
||||
(type->AsVector() && type->AsVector()->element_type()->AsInteger())) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetFloatInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
if (type->AsFloat() ||
|
||||
(type->AsVector() && type->AsVector()->element_type()->AsFloat())) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetBooleanInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
if (type->AsBool() ||
|
||||
(type->AsVector() && type->AsVector()->element_type()->AsBool())) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::RestrictToVectorWidth(
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t vector_width) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
// Get the vector width of |inst|, which is 1 if |inst| is a scalar and is
|
||||
// otherwise derived from its vector type.
|
||||
uint32_t other_vector_width =
|
||||
type->AsVector() ? type->AsVector()->element_count() : 1;
|
||||
// Keep |inst| if the vector widths match.
|
||||
if (vector_width == other_vector_width) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t bit_width) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
const opt::analysis::Type* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
if (type->AsVector()) {
|
||||
type = type->AsVector()->element_type();
|
||||
}
|
||||
assert((type->AsInteger() || type->AsFloat()) &&
|
||||
"Precondition: all input instructions must "
|
||||
"have integer or float scalar or vector type.");
|
||||
if ((type->AsInteger() && type->AsInteger()->width() == bit_width) ||
|
||||
(type->AsFloat() && type->AsFloat()->width() == bit_width)) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -1,80 +0,0 @@
|
||||
// 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_EQUATION_INSTRUCTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that sprinkles instructions through the module that define
|
||||
// equations using various arithmetic and logical operators.
|
||||
class FuzzerPassAddEquationInstructions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddEquationInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddEquationInstructions();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Yields those instructions in |instructions| that have integer scalar or
|
||||
// vector result type.
|
||||
std::vector<opt::Instruction*> GetIntegerInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Returns only instructions, that have either a scalar floating-point or a
|
||||
// vector type.
|
||||
std::vector<opt::Instruction*> GetFloatInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Yields those instructions in |instructions| that have boolean scalar or
|
||||
// vector result type.
|
||||
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.
|
||||
std::vector<opt::Instruction*> RestrictToVectorWidth(
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t vector_width) const;
|
||||
|
||||
// Requires that |instructions| are integer or float scalars or vectors.
|
||||
// Returns only those instructions for which the bit-width of the underlying
|
||||
// integer or floating-point type is |bit_width|.
|
||||
std::vector<opt::Instruction*> RestrictToElementBitWidth(
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t bit_width) const;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
|
||||
@@ -1,199 +0,0 @@
|
||||
// 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_function_calls.h"
|
||||
|
||||
#include "source/fuzz/call_graph.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_function_call.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
|
||||
|
||||
void FuzzerPassAddFunctionCalls::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
// Check whether it is legitimate to insert a function call before the
|
||||
// instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
|
||||
inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting a function call here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfCallingFunction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the module's call graph - we don't cache it since it may
|
||||
// change each time we apply a transformation. If this proves to be
|
||||
// a bottleneck the call graph data structure could be made updatable.
|
||||
CallGraph call_graph(GetIRContext());
|
||||
|
||||
// Gather all the non-entry point functions different from this
|
||||
// function. It is important to ignore entry points as a function
|
||||
// cannot be an entry point and the target of an OpFunctionCall
|
||||
// instruction. We ignore this function to avoid direct recursion.
|
||||
std::vector<opt::Function*> candidate_functions;
|
||||
for (auto& other_function : *GetIRContext()->module()) {
|
||||
if (&other_function != function &&
|
||||
!fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
|
||||
other_function.result_id())) {
|
||||
candidate_functions.push_back(&other_function);
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a function to call, at random, by considering candidate
|
||||
// functions until a suitable one is found.
|
||||
opt::Function* chosen_function = nullptr;
|
||||
while (!candidate_functions.empty()) {
|
||||
opt::Function* candidate_function =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions);
|
||||
if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
|
||||
block->id()) &&
|
||||
!GetTransformationContext()->GetFactManager()->FunctionIsLivesafe(
|
||||
candidate_function->result_id())) {
|
||||
// Unless in a dead block, only livesafe functions can be invoked
|
||||
continue;
|
||||
}
|
||||
if (call_graph.GetIndirectCallees(candidate_function->result_id())
|
||||
.count(function->result_id())) {
|
||||
// Calling this function could lead to indirect recursion
|
||||
continue;
|
||||
}
|
||||
chosen_function = candidate_function;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!chosen_function) {
|
||||
// No suitable function was found to call. (This can happen, for
|
||||
// instance, if the current function is the only function in the
|
||||
// module.)
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationFunctionCall(
|
||||
GetFuzzerContext()->GetFreshId(), chosen_function->result_id(),
|
||||
ChooseFunctionCallArguments(*chosen_function, function, block,
|
||||
inst_it),
|
||||
instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
|
||||
const opt::Function& callee, opt::Function* caller_function,
|
||||
opt::BasicBlock* caller_block,
|
||||
const opt::BasicBlock::iterator& caller_inst_it) {
|
||||
auto available_pointers = FindAvailableInstructions(
|
||||
caller_function, caller_block, caller_inst_it,
|
||||
[this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) {
|
||||
if (inst->opcode() != SpvOpVariable ||
|
||||
inst->opcode() != SpvOpFunctionParameter) {
|
||||
// Function parameters and variables are the only
|
||||
// kinds of pointer that can be used as actual
|
||||
// parameters.
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetTransformationContext()->GetFactManager()->BlockIsDead(
|
||||
caller_block->id()) ||
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->PointeeValueIsIrrelevant(inst->result_id());
|
||||
});
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<uint32_t>> type_id_to_result_id;
|
||||
for (const auto* inst : available_pointers) {
|
||||
type_id_to_result_id[inst->type_id()].push_back(inst->result_id());
|
||||
}
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
for (const auto* param :
|
||||
fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) {
|
||||
const auto* param_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(param->type_id());
|
||||
assert(param_type && "Parameter has invalid type");
|
||||
|
||||
if (!param_type->AsPointer()) {
|
||||
// We mark the constant as irrelevant so that we can replace it with a
|
||||
// more interesting value later.
|
||||
result.push_back(FindOrCreateZeroConstant(param->type_id(), true));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type_id_to_result_id.count(param->type_id())) {
|
||||
// Use an existing pointer if there are any.
|
||||
const auto& candidates = type_id_to_result_id[param->type_id()];
|
||||
result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make a new variable, at function or global scope depending on the storage
|
||||
// class of the pointer.
|
||||
|
||||
// Get a fresh id for the new variable.
|
||||
uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
|
||||
|
||||
// The id of this variable is what we pass as the parameter to
|
||||
// the call.
|
||||
result.push_back(fresh_variable_id);
|
||||
type_id_to_result_id[param->type_id()].push_back(fresh_variable_id);
|
||||
|
||||
// Now bring the variable into existence.
|
||||
auto storage_class = param_type->AsPointer()->storage_class();
|
||||
auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), param->type_id());
|
||||
if (storage_class == SpvStorageClassFunction) {
|
||||
// Add a new zero-initialized local variable to the current
|
||||
// function, noting that its pointee value is irrelevant.
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
fresh_variable_id, param->type_id(), caller_function->result_id(),
|
||||
FindOrCreateZeroConstant(pointee_type_id, false), true));
|
||||
} else {
|
||||
assert((storage_class == SpvStorageClassPrivate ||
|
||||
storage_class == SpvStorageClassWorkgroup) &&
|
||||
"Only Function, Private and Workgroup storage classes are "
|
||||
"supported at present.");
|
||||
// Add a new global variable to the module, zero-initializing it if
|
||||
// it has Private storage class, and noting that its pointee value is
|
||||
// irrelevant.
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
fresh_variable_id, param->type_id(), storage_class,
|
||||
storage_class == SpvStorageClassPrivate
|
||||
? FindOrCreateZeroConstant(pointee_type_id, false)
|
||||
: 0,
|
||||
true));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,50 +0,0 @@
|
||||
// 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_FUNCTION_CALLS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that adds calls at random to (a) livesafe functions, from
|
||||
// anywhere, and (b) any functions, from dead blocks.
|
||||
class FuzzerPassAddFunctionCalls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddFunctionCalls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddFunctionCalls();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Randomly chooses suitable arguments to invoke |callee| right before
|
||||
// instruction |caller_inst_it| of block |caller_block| in |caller_function|,
|
||||
// based on both existing available instructions and the addition of new
|
||||
// instructions to the module.
|
||||
std::vector<uint32_t> ChooseFunctionCallArguments(
|
||||
const opt::Function& callee, opt::Function* caller_function,
|
||||
opt::BasicBlock* caller_block,
|
||||
const opt::BasicBlock::iterator& caller_inst_it);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
|
||||
@@ -1,95 +0,0 @@
|
||||
// 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_global_variables.h"
|
||||
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_type_pointer.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
|
||||
|
||||
void FuzzerPassAddGlobalVariables::Apply() {
|
||||
SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
|
||||
for (auto& entry_point : GetIRContext()->module()->entry_points()) {
|
||||
// If the execution model of some entry point is GLCompute,
|
||||
// then the variable storage class may be Workgroup.
|
||||
if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) {
|
||||
variable_storage_class =
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass())
|
||||
? SpvStorageClassWorkgroup
|
||||
: SpvStorageClassPrivate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto basic_type_ids_and_pointers =
|
||||
GetAvailableBasicTypesAndPointers(variable_storage_class);
|
||||
|
||||
// These are the basic types that are available to this fuzzer pass.
|
||||
auto& basic_types = basic_type_ids_and_pointers.first;
|
||||
|
||||
// These are the pointers to those basic types that are *initially* available
|
||||
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||
// none are available for a given basic type.
|
||||
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||
|
||||
// Probabilistically keep adding global variables.
|
||||
while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
|
||||
// Choose a random basic type; the new variable's type will be a pointer to
|
||||
// this basic type.
|
||||
uint32_t basic_type =
|
||||
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||
uint32_t pointer_type_id;
|
||||
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||
basic_type_to_pointers.at(basic_type);
|
||||
// Determine whether there is at least one pointer to this basic type.
|
||||
if (available_pointers_to_basic_type.empty()) {
|
||||
// There is not. Make one, to use here, and add it to the available
|
||||
// pointers for the basic type so that future variables can potentially
|
||||
// use it.
|
||||
pointer_type_id = GetFuzzerContext()->GetFreshId();
|
||||
available_pointers_to_basic_type.push_back(pointer_type_id);
|
||||
ApplyTransformation(TransformationAddTypePointer(
|
||||
pointer_type_id, variable_storage_class, basic_type));
|
||||
} else {
|
||||
// There is - grab one.
|
||||
pointer_type_id =
|
||||
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_basic_type)];
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationAddGlobalVariable(
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type_id,
|
||||
variable_storage_class,
|
||||
variable_storage_class == SpvStorageClassPrivate
|
||||
? FindOrCreateZeroConstant(basic_type, false)
|
||||
: 0,
|
||||
true));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// 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_GLOBAL_VARIABLES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that randomly adds global variables, with Private storage class,
|
||||
// to the module.
|
||||
class FuzzerPassAddGlobalVariables : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddGlobalVariables(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddGlobalVariables();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
|
||||
@@ -1,200 +0,0 @@
|
||||
// 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_add_image_sample_unused_components.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
|
||||
#include "source/fuzz/transformation_composite_construct.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddImageSampleUnusedComponents::
|
||||
FuzzerPassAddImageSampleUnusedComponents(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddImageSampleUnusedComponents::
|
||||
~FuzzerPassAddImageSampleUnusedComponents() = default;
|
||||
|
||||
void FuzzerPassAddImageSampleUnusedComponents::Apply() {
|
||||
// SPIR-V module to help understand the transformation.
|
||||
//
|
||||
// OpCapability Shader
|
||||
// %1 = OpExtInstImport "GLSL.std.450"
|
||||
// OpMemoryModel Logical GLSL450
|
||||
// OpEntryPoint Fragment %15 "main" %12 %14
|
||||
// OpExecutionMode %15 OriginUpperLeft
|
||||
//
|
||||
// ; Decorations
|
||||
// OpDecorate %12 Location 0 ; Input color variable location
|
||||
// OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
|
||||
// descriptor set OpDecorate %13 Binding 0 ; Image coordinate
|
||||
// variable binding OpDecorate %14 Location 0 ; Fragment color
|
||||
// variable location
|
||||
//
|
||||
// ; Types
|
||||
// %2 = OpTypeVoid
|
||||
// %3 = OpTypeFunction %2
|
||||
// %4 = OpTypeFloat 32
|
||||
// %5 = OpTypeVector %4 2
|
||||
// %6 = OpTypeVector %4 4
|
||||
// %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
|
||||
// %8 = OpTypeSampledImage %7
|
||||
// %9 = OpTypePointer Input %5
|
||||
// %10 = OpTypePointer UniformConstant %8
|
||||
// %11 = OpTypePointer Output %6
|
||||
//
|
||||
// ; Variables
|
||||
// %12 = OpVariable %9 Input ; Input image coordinate variable
|
||||
// %13 = OpVariable %10 UniformConstant ; Image variable
|
||||
// %14 = OpVariable %11 Output ; Fragment color variable
|
||||
//
|
||||
// ; main function
|
||||
// %15 = OpFunction %2 None %3
|
||||
// %16 = OpLabel
|
||||
// %17 = OpLoad %5 %12
|
||||
// %18 = OpLoad %8 %13
|
||||
// %19 = OpImageSampleImplicitLod %6 %18 %17
|
||||
// OpStore %14 %19
|
||||
// OpReturn
|
||||
// OpFunctionEnd
|
||||
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
// |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
|
||||
if (!spvOpcodeIsImageSample(instruction->opcode())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfAddingImageSampleUnusedComponents())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gets image sample coordinate information.
|
||||
// |coordinate_instruction| %17 = OpLoad %5 %12
|
||||
uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
|
||||
auto coordinate_instruction =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
|
||||
auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
|
||||
coordinate_instruction->type_id());
|
||||
|
||||
// If the coordinate is a 4-dimensional vector, then no unused components
|
||||
// may be added.
|
||||
if (coordinate_type->AsVector() &&
|
||||
coordinate_type->AsVector()->element_count() == 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the coordinate is a scalar, then at most 3 unused components may be
|
||||
// added. If the coordinate is a vector, then the maximum number of unused
|
||||
// components depends on the vector size.
|
||||
// For the sample module, the coordinate type instruction is %5 =
|
||||
// OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
|
||||
uint32_t max_unused_component_count =
|
||||
coordinate_type->AsInteger() || coordinate_type->AsFloat()
|
||||
? 3
|
||||
: 4 - coordinate_type->AsVector()->element_count();
|
||||
|
||||
// |unused_component_count| may be 1 or 2.
|
||||
uint32_t unused_component_count =
|
||||
GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
|
||||
max_unused_component_count);
|
||||
|
||||
// Gets a type for the zero-unused components.
|
||||
uint32_t zero_constant_type_id;
|
||||
switch (unused_component_count) {
|
||||
case 1:
|
||||
// If the coordinate is an integer or float, then the unused components
|
||||
// type is the same as the coordinate. If the coordinate is a vector,
|
||||
// then the unused components type is the same as the vector components
|
||||
// type.
|
||||
zero_constant_type_id =
|
||||
coordinate_type->AsInteger() || coordinate_type->AsFloat()
|
||||
? coordinate_instruction->type_id()
|
||||
: GetIRContext()->get_type_mgr()->GetId(
|
||||
coordinate_type->AsVector()->element_type());
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
// If the coordinate is an integer or float, then the unused components
|
||||
// type is the same as the coordinate. If the coordinate is a vector,
|
||||
// then the unused components type is the same as the coordinate
|
||||
// components type.
|
||||
// |zero_constant_type_id| %5 = OpTypeVector %4 2
|
||||
zero_constant_type_id =
|
||||
coordinate_type->AsInteger() || coordinate_type->AsFloat()
|
||||
? FindOrCreateVectorType(coordinate_instruction->type_id(),
|
||||
unused_component_count)
|
||||
: FindOrCreateVectorType(
|
||||
GetIRContext()->get_type_mgr()->GetId(
|
||||
coordinate_type->AsVector()->element_type()),
|
||||
unused_component_count);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable.");
|
||||
zero_constant_type_id = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Gets |coordinate_type| again because the module may have changed due to
|
||||
// the use of FindOrCreateVectorType above.
|
||||
coordinate_type = GetIRContext()->get_type_mgr()->GetType(
|
||||
coordinate_instruction->type_id());
|
||||
|
||||
// If the new vector type with unused components does not exist, then create
|
||||
// it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
|
||||
uint32_t coordinate_with_unused_components_type_id =
|
||||
coordinate_type->AsInteger() || coordinate_type->AsFloat()
|
||||
? FindOrCreateVectorType(coordinate_instruction->type_id(),
|
||||
1 + unused_component_count)
|
||||
: FindOrCreateVectorType(
|
||||
GetIRContext()->get_type_mgr()->GetId(
|
||||
coordinate_type->AsVector()->element_type()),
|
||||
coordinate_type->AsVector()->element_count() +
|
||||
unused_component_count);
|
||||
|
||||
// Inserts an OpCompositeConstruct instruction which
|
||||
// represents the coordinate with unused components.
|
||||
// |coordinate_with_unused_components_id|
|
||||
// %22 = OpCompositeConstruct %6 %17 %21
|
||||
uint32_t coordinate_with_unused_components_id =
|
||||
GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationCompositeConstruct(
|
||||
coordinate_with_unused_components_type_id,
|
||||
{coordinate_instruction->result_id(),
|
||||
// FindOrCreateZeroConstant
|
||||
// %20 = OpConstant %4 0
|
||||
// %21 = OpConstantComposite %5 %20 %20
|
||||
FindOrCreateZeroConstant(zero_constant_type_id, false)},
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction),
|
||||
coordinate_with_unused_components_id));
|
||||
|
||||
// Tries to add unused components to the image sample coordinate.
|
||||
// %19 = OpImageSampleImplicitLod %6 %18 %22
|
||||
ApplyTransformation(TransformationAddImageSampleUnusedComponents(
|
||||
coordinate_with_unused_components_id,
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction)));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// This fuzzer pass searches for image sample instructions in the module and
|
||||
// randomly applies the transformation to add unused components to the image
|
||||
// sample coordinate.
|
||||
class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddImageSampleUnusedComponents(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddImageSampleUnusedComponents();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
|
||||
@@ -1,96 +0,0 @@
|
||||
// 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_loads.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_load.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddLoads::FuzzerPassAddLoads(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
|
||||
|
||||
void FuzzerPassAddLoads::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(inst_it->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Check whether it is legitimate to insert a load before this
|
||||
// instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting a load here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingLoad())) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> relevant_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[](opt::IRContext* context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
switch (instruction->opcode()) {
|
||||
case SpvOpConstantNull:
|
||||
case SpvOpUndef:
|
||||
// Do not allow loading from a null or undefined pointer;
|
||||
// this might be OK if the block is dead, but for now we
|
||||
// conservatively avoid it.
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return context->get_def_use_mgr()
|
||||
->GetDef(instruction->type_id())
|
||||
->opcode() == SpvOpTypePointer;
|
||||
});
|
||||
|
||||
// At this point, |relevant_instructions| contains all the pointers
|
||||
// we might think of loading from.
|
||||
if (relevant_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose a pointer at random, and create and apply a loading
|
||||
// transformation based on it.
|
||||
ApplyTransformation(TransformationLoad(
|
||||
GetFuzzerContext()->GetFreshId(),
|
||||
relevant_instructions[GetFuzzerContext()->RandomIndex(
|
||||
relevant_instructions)]
|
||||
->result_id(),
|
||||
instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// 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_LOADS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that adds stores, at random, from pointers in the module.
|
||||
class FuzzerPassAddLoads : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddLoads(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddLoads();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
|
||||
@@ -1,80 +0,0 @@
|
||||
// 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_local_variables.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_type_pointer.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
|
||||
|
||||
void FuzzerPassAddLocalVariables::Apply() {
|
||||
auto basic_type_ids_and_pointers =
|
||||
GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
|
||||
|
||||
// These are the basic types that are available to this fuzzer pass.
|
||||
auto& basic_types = basic_type_ids_and_pointers.first;
|
||||
|
||||
// These are the pointers to those basic types that are *initially* available
|
||||
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||
// none are available for a given basic type.
|
||||
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||
|
||||
// Consider every function in the module.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// Probabilistically keep adding random variables to this function.
|
||||
while (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
|
||||
// Choose a random basic type; the new variable's type will be a pointer
|
||||
// to this basic type.
|
||||
uint32_t basic_type =
|
||||
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||
uint32_t pointer_type;
|
||||
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||
basic_type_to_pointers.at(basic_type);
|
||||
// Determine whether there is at least one pointer to this basic type.
|
||||
if (available_pointers_to_basic_type.empty()) {
|
||||
// There is not. Make one, to use here, and add it to the available
|
||||
// pointers for the basic type so that future variables can potentially
|
||||
// use it.
|
||||
pointer_type = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddTypePointer(
|
||||
pointer_type, SpvStorageClassFunction, basic_type));
|
||||
available_pointers_to_basic_type.push_back(pointer_type);
|
||||
} else {
|
||||
// There is - grab one.
|
||||
pointer_type =
|
||||
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||
available_pointers_to_basic_type)];
|
||||
}
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
|
||||
FindOrCreateZeroConstant(basic_type, false), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// 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_LOCAL_VARIABLES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that randomly adds local variables, with Function storage class,
|
||||
// to the module.
|
||||
class FuzzerPassAddLocalVariables : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddLocalVariables(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddLocalVariables();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
|
||||
@@ -1,66 +0,0 @@
|
||||
// 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
|
||||
@@ -1,43 +0,0 @@
|
||||
// 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
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2019 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_no_contraction_decorations.h"
|
||||
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddNoContractionDecorations::
|
||||
~FuzzerPassAddNoContractionDecorations() = default;
|
||||
|
||||
void FuzzerPassAddNoContractionDecorations::Apply() {
|
||||
// Consider every instruction in every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& inst : block) {
|
||||
// Restrict attention to arithmetic instructions (as defined in the
|
||||
// SPIR-V specification).
|
||||
if (TransformationAddNoContractionDecoration::IsArithmetic(
|
||||
inst.opcode())) {
|
||||
// Randomly choose whether to apply the NoContraction decoration to
|
||||
// this arithmetic instruction.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfAddingNoContractionDecoration())) {
|
||||
TransformationAddNoContractionDecoration transformation(
|
||||
inst.result_id());
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_NO_CONTRACTION_DECORATIONS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that applies the NoContraction decoration to arithmetic instructions.
|
||||
class FuzzerPassAddNoContractionDecorations : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddNoContractionDecorations(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddNoContractionDecorations() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_
|
||||
@@ -1,297 +0,0 @@
|
||||
// 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_opphi_synonyms.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_add_opphi_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddOpPhiSynonyms::~FuzzerPassAddOpPhiSynonyms() = default;
|
||||
|
||||
void FuzzerPassAddOpPhiSynonyms::Apply() {
|
||||
// Get a list of synonymous ids with the same type that can be used in the
|
||||
// same OpPhi instruction.
|
||||
auto equivalence_classes = GetIdEquivalenceClasses();
|
||||
|
||||
// Make a list of references, to avoid copying sets unnecessarily.
|
||||
std::vector<std::set<uint32_t>*> equivalence_class_pointers;
|
||||
for (auto& set : equivalence_classes) {
|
||||
equivalence_class_pointers.push_back(&set);
|
||||
}
|
||||
|
||||
// Keep a list of transformations to apply at the end.
|
||||
std::vector<TransformationAddOpPhiSynonym> transformations_to_apply;
|
||||
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// Randomly decide whether to consider this block.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingOpPhiSynonym())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The block must have at least one predecessor.
|
||||
size_t num_preds = GetIRContext()->cfg()->preds(block.id()).size();
|
||||
if (num_preds == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::set<uint32_t>* chosen_equivalence_class = nullptr;
|
||||
|
||||
if (num_preds > 1) {
|
||||
// If the block has more than one predecessor, prioritise sets with at
|
||||
// least 2 ids available at some predecessor.
|
||||
chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly(
|
||||
equivalence_class_pointers, block.id(), 2);
|
||||
}
|
||||
|
||||
// If a set was not already chosen, choose one with at least one available
|
||||
// id.
|
||||
if (!chosen_equivalence_class) {
|
||||
chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly(
|
||||
equivalence_class_pointers, block.id(), 1);
|
||||
}
|
||||
|
||||
// If no suitable set was found, we cannot apply the transformation to
|
||||
// this block.
|
||||
if (!chosen_equivalence_class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialise the map from predecessor labels to ids.
|
||||
std::map<uint32_t, uint32_t> preds_to_ids;
|
||||
|
||||
// Keep track of the ids used and of the id of a predecessor with at least
|
||||
// two ids to choose from. This is to ensure that, if possible, at least
|
||||
// two distinct ids will be used.
|
||||
std::set<uint32_t> ids_chosen;
|
||||
uint32_t pred_with_alternatives = 0;
|
||||
|
||||
// Choose an id for each predecessor.
|
||||
for (uint32_t pred_id : GetIRContext()->cfg()->preds(block.id())) {
|
||||
auto suitable_ids = GetSuitableIds(*chosen_equivalence_class, pred_id);
|
||||
assert(!suitable_ids.empty() &&
|
||||
"We must be able to find at least one suitable id because the "
|
||||
"equivalence class was chosen among suitable ones.");
|
||||
|
||||
// If this predecessor has more than one id to choose from and it is the
|
||||
// first one of this kind that we found, remember its id.
|
||||
if (suitable_ids.size() > 1 && !pred_with_alternatives) {
|
||||
pred_with_alternatives = pred_id;
|
||||
}
|
||||
|
||||
uint32_t chosen_id =
|
||||
suitable_ids[GetFuzzerContext()->RandomIndex(suitable_ids)];
|
||||
|
||||
// Add this id to the set of ids chosen.
|
||||
ids_chosen.emplace(chosen_id);
|
||||
|
||||
// Add the pair (predecessor, chosen id) to the map.
|
||||
preds_to_ids[pred_id] = chosen_id;
|
||||
}
|
||||
|
||||
// If:
|
||||
// - the block has more than one predecessor
|
||||
// - at least one predecessor has more than one alternative
|
||||
// - the same id has been chosen by all the predecessors
|
||||
// then choose another one for the predecessor with more than one
|
||||
// alternative.
|
||||
if (num_preds > 1 && pred_with_alternatives != 0 &&
|
||||
ids_chosen.size() == 1) {
|
||||
auto suitable_ids =
|
||||
GetSuitableIds(*chosen_equivalence_class, pred_with_alternatives);
|
||||
uint32_t chosen_id =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids);
|
||||
if (chosen_id == preds_to_ids[pred_with_alternatives]) {
|
||||
chosen_id = GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids);
|
||||
}
|
||||
|
||||
preds_to_ids[pred_with_alternatives] = chosen_id;
|
||||
}
|
||||
|
||||
// Add the transformation to the list of transformations to apply.
|
||||
transformations_to_apply.emplace_back(block.id(), preds_to_ids,
|
||||
GetFuzzerContext()->GetFreshId());
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the transformations.
|
||||
for (const auto& transformation : transformations_to_apply) {
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::set<uint32_t>>
|
||||
FuzzerPassAddOpPhiSynonyms::GetIdEquivalenceClasses() {
|
||||
std::vector<std::set<uint32_t>> id_equivalence_classes;
|
||||
|
||||
// Keep track of all the ids that have already be assigned to a class.
|
||||
std::set<uint32_t> already_in_a_class;
|
||||
|
||||
for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) {
|
||||
// Exclude ids that have already been assigned to a class.
|
||||
if (already_in_a_class.count(pair.first)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude irrelevant ids.
|
||||
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
pair.first)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude ids having a type that is not allowed by the transformation.
|
||||
if (!TransformationAddOpPhiSynonym::CheckTypeIsAllowed(
|
||||
GetIRContext(), pair.second->type_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need a new equivalence class for this id.
|
||||
std::set<uint32_t> new_equivalence_class;
|
||||
|
||||
// Add this id to the class.
|
||||
new_equivalence_class.emplace(pair.first);
|
||||
already_in_a_class.emplace(pair.first);
|
||||
|
||||
// Add all the synonyms with the same type to this class.
|
||||
for (auto synonym :
|
||||
GetTransformationContext()->GetFactManager()->GetSynonymsForId(
|
||||
pair.first)) {
|
||||
// The synonym must be a plain id - it cannot be an indexed access into a
|
||||
// composite.
|
||||
if (synonym->index_size() > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The synonym must not be irrelevant.
|
||||
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
synonym->object())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto synonym_def =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(synonym->object());
|
||||
// The synonym must exist and have the same type as the id we are
|
||||
// considering.
|
||||
if (!synonym_def || synonym_def->type_id() != pair.second->type_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can add this synonym to the new equivalence class.
|
||||
new_equivalence_class.emplace(synonym->object());
|
||||
already_in_a_class.emplace(synonym->object());
|
||||
}
|
||||
|
||||
// Add the new equivalence class to the list of equivalence classes.
|
||||
id_equivalence_classes.emplace_back(std::move(new_equivalence_class));
|
||||
}
|
||||
|
||||
return id_equivalence_classes;
|
||||
}
|
||||
|
||||
bool FuzzerPassAddOpPhiSynonyms::EquivalenceClassIsSuitableForBlock(
|
||||
const std::set<uint32_t>& equivalence_class, uint32_t block_id,
|
||||
uint32_t distinct_ids_required) {
|
||||
bool at_least_one_id_for_each_pred = true;
|
||||
|
||||
// Keep a set of the suitable ids found.
|
||||
std::set<uint32_t> suitable_ids_found;
|
||||
|
||||
// Loop through all the predecessors of the block.
|
||||
for (auto pred_id : GetIRContext()->cfg()->preds(block_id)) {
|
||||
// Find the last instruction in the predecessor block.
|
||||
auto last_instruction =
|
||||
GetIRContext()->get_instr_block(pred_id)->terminator();
|
||||
|
||||
// Initially assume that there is not a suitable id for this predecessor.
|
||||
bool at_least_one_suitable_id_found = false;
|
||||
for (uint32_t id : equivalence_class) {
|
||||
if (fuzzerutil::IdIsAvailableBeforeInstruction(GetIRContext(),
|
||||
last_instruction, id)) {
|
||||
// We have found a suitable id.
|
||||
at_least_one_suitable_id_found = true;
|
||||
suitable_ids_found.emplace(id);
|
||||
|
||||
// If we have already found enough distinct suitable ids, we don't need
|
||||
// to check the remaining ones for this predecessor.
|
||||
if (suitable_ids_found.size() >= distinct_ids_required) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no suitable id was found for this predecessor, this equivalence class
|
||||
// is not suitable and we don't need to check the other predecessors.
|
||||
if (!at_least_one_suitable_id_found) {
|
||||
at_least_one_id_for_each_pred = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The equivalence class is suitable if at least one suitable id was found for
|
||||
// each predecessor and we have found at least |distinct_ids_required|
|
||||
// distinct suitable ids in general.
|
||||
return at_least_one_id_for_each_pred &&
|
||||
suitable_ids_found.size() >= distinct_ids_required;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FuzzerPassAddOpPhiSynonyms::GetSuitableIds(
|
||||
const std::set<uint32_t>& ids, uint32_t pred_id) {
|
||||
// Initialise an empty vector of suitable ids.
|
||||
std::vector<uint32_t> suitable_ids;
|
||||
|
||||
// Get the predecessor block.
|
||||
auto predecessor = fuzzerutil::MaybeFindBlock(GetIRContext(), pred_id);
|
||||
|
||||
// Loop through the ids to find the suitable ones.
|
||||
for (uint32_t id : ids) {
|
||||
if (fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
GetIRContext(), predecessor->terminator(), id)) {
|
||||
suitable_ids.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
return suitable_ids;
|
||||
}
|
||||
|
||||
std::set<uint32_t>*
|
||||
FuzzerPassAddOpPhiSynonyms::MaybeFindSuitableEquivalenceClassRandomly(
|
||||
const std::vector<std::set<uint32_t>*>& candidates, uint32_t block_id,
|
||||
uint32_t distinct_ids_required) {
|
||||
auto remaining_candidates = candidates;
|
||||
while (!remaining_candidates.empty()) {
|
||||
// Choose one set randomly and return it if it is suitable.
|
||||
auto chosen =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&remaining_candidates);
|
||||
if (EquivalenceClassIsSuitableForBlock(*chosen, block_id,
|
||||
distinct_ids_required)) {
|
||||
return chosen;
|
||||
}
|
||||
}
|
||||
|
||||
// No suitable sets were found.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,73 +0,0 @@
|
||||
// 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_OPPHI_SYNONYMS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass to add OpPhi instructions which can take the values of ids that
|
||||
// have been marked as synonymous. This instruction will itself be marked as
|
||||
// synonymous with the others.
|
||||
class FuzzerPassAddOpPhiSynonyms : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddOpPhiSynonyms(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddOpPhiSynonyms() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
// Computes the equivalence classes for the non-pointer and non-irrelevant ids
|
||||
// in the module, where two ids are considered equivalent iff they have been
|
||||
// declared synonymous and they have the same type.
|
||||
std::vector<std::set<uint32_t>> GetIdEquivalenceClasses();
|
||||
|
||||
// Returns true iff |equivalence_class| contains at least
|
||||
// |distinct_ids_required| ids so that all of these ids are available at the
|
||||
// end of at least one predecessor of the block with label |block_id|.
|
||||
// Assumes that the block has at least one predecessor.
|
||||
bool EquivalenceClassIsSuitableForBlock(
|
||||
const std::set<uint32_t>& equivalence_class, uint32_t block_id,
|
||||
uint32_t distinct_ids_required);
|
||||
|
||||
// Returns a vector with the ids that are available to use at the end of the
|
||||
// block with id |pred_id|, selected among the given |ids|. Assumes that
|
||||
// |pred_id| is the label of a block and all ids in |ids| exist in the module.
|
||||
std::vector<uint32_t> GetSuitableIds(const std::set<uint32_t>& ids,
|
||||
uint32_t pred_id);
|
||||
|
||||
private:
|
||||
// Randomly chooses one of the equivalence classes in |candidates|, so that it
|
||||
// satisfies all of the following conditions:
|
||||
// - For each of the predecessors of the |block_id| block, there is at least
|
||||
// one id in the chosen equivalence class that is available at the end of
|
||||
// it.
|
||||
// - There are at least |distinct_ids_required| ids available at the end of
|
||||
// some predecessor.
|
||||
// Returns nullptr if no equivalence class in |candidates| satisfies the
|
||||
// requirements.
|
||||
std::set<uint32_t>* MaybeFindSuitableEquivalenceClassRandomly(
|
||||
const std::vector<std::set<uint32_t>*>& candidates, uint32_t block_id,
|
||||
uint32_t distinct_ids_required);
|
||||
};
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_
|
||||
@@ -1,147 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_add_parameters.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddParameters::FuzzerPassAddParameters(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
|
||||
|
||||
void FuzzerPassAddParameters::Apply() {
|
||||
// Compute type candidates for the new parameter.
|
||||
std::vector<uint32_t> type_candidates;
|
||||
for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(type_inst->result_id());
|
||||
assert(type && "Type instruction is not registered in the type manager");
|
||||
if (TransformationAddParameter::IsParameterTypeSupported(*type)) {
|
||||
type_candidates.push_back(type_inst->result_id());
|
||||
}
|
||||
}
|
||||
|
||||
if (type_candidates.empty()) {
|
||||
// The module contains no suitable types to use in new parameters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all functions in the module.
|
||||
for (const auto& function : *GetIRContext()->module()) {
|
||||
// Skip all entry-point functions - we don't want to change those.
|
||||
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
|
||||
function.result_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetNumberOfParameters(function) >=
|
||||
GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingParameters())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto num_new_parameters =
|
||||
GetFuzzerContext()->GetRandomNumberOfNewParameters(
|
||||
GetNumberOfParameters(function));
|
||||
|
||||
for (uint32_t i = 0; i < num_new_parameters; ++i) {
|
||||
auto current_type_id =
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
|
||||
auto current_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(current_type_id);
|
||||
std::map<uint32_t, uint32_t> call_parameter_ids;
|
||||
|
||||
// Consider the case when a pointer type was selected.
|
||||
if (current_type->kind() == opt::analysis::Type::kPointer) {
|
||||
auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
|
||||
GetIRContext(), current_type_id);
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassFunction: {
|
||||
// In every caller find or create a local variable that has the
|
||||
// selected type.
|
||||
for (auto* instr :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
auto block = GetIRContext()->get_instr_block(instr);
|
||||
auto function_id = block->GetParent()->result_id();
|
||||
uint32_t variable_id =
|
||||
FindOrCreateLocalVariable(current_type_id, function_id, true);
|
||||
call_parameter_ids[instr->result_id()] = variable_id;
|
||||
}
|
||||
} break;
|
||||
case SpvStorageClassPrivate:
|
||||
case SpvStorageClassWorkgroup: {
|
||||
// If there exists at least one caller, find or create a global
|
||||
// variable that has the selected type.
|
||||
std::vector<opt::Instruction*> callers =
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id());
|
||||
if (!callers.empty()) {
|
||||
uint32_t variable_id =
|
||||
FindOrCreateGlobalVariable(current_type_id, true);
|
||||
for (auto* instr : callers) {
|
||||
call_parameter_ids[instr->result_id()] = variable_id;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If there exists at least one caller, find or create a zero constant
|
||||
// that has the selected type.
|
||||
std::vector<opt::Instruction*> callers =
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id());
|
||||
if (!callers.empty()) {
|
||||
uint32_t constant_id =
|
||||
FindOrCreateZeroConstant(current_type_id, true);
|
||||
for (auto* instr :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
call_parameter_ids[instr->result_id()] = constant_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationAddParameter(
|
||||
function.result_id(), GetFuzzerContext()->GetFreshId(),
|
||||
current_type_id, std::move(call_parameter_ids),
|
||||
GetFuzzerContext()->GetFreshId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
|
||||
const opt::Function& function) const {
|
||||
const auto* type = GetIRContext()->get_type_mgr()->GetType(
|
||||
function.DefInst().GetSingleWordInOperand(1));
|
||||
assert(type && type->AsFunction());
|
||||
|
||||
return static_cast<uint32_t>(type->AsFunction()->param_types().size());
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Randomly decides for each non-entry-point function in the module whether to
|
||||
// add new parameters to it. If so, randomly determines the number of parameters
|
||||
// to add, their type and creates constants used to initialize them.
|
||||
class FuzzerPassAddParameters : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddParameters(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddParameters() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Returns number of parameters of |function|.
|
||||
uint32_t GetNumberOfParameters(const opt::Function& function) const;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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_relaxed_decorations.h"
|
||||
|
||||
#include "source/fuzz/transformation_add_relaxed_decoration.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default;
|
||||
|
||||
void FuzzerPassAddRelaxedDecorations::Apply() {
|
||||
// Consider every instruction in every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& inst : block) {
|
||||
// Randomly choose whether to apply the RelaxedPrecision decoration
|
||||
// to this instruction.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) {
|
||||
TransformationAddRelaxedDecoration transformation(inst.result_id());
|
||||
// Restrict attention to numeric instructions (returning 32-bit
|
||||
// floats or ints according to SPIR-V documentation) in dead blocks.
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that applies the Relaxed decoration to numeric instructions.
|
||||
class FuzzerPassAddRelaxedDecorations : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddRelaxedDecorations(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddRelaxedDecorations() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
|
||||
@@ -1,132 +0,0 @@
|
||||
// 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_stores.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_store.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddStores::FuzzerPassAddStores(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddStores::~FuzzerPassAddStores() = default;
|
||||
|
||||
void FuzzerPassAddStores::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(inst_it->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Check whether it is legitimate to insert a store before this
|
||||
// instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
|
||||
inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting a store here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingStore())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for pointers we might consider storing to.
|
||||
std::vector<opt::Instruction*> relevant_pointers =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this, block](opt::IRContext* context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
auto type_inst = context->get_def_use_mgr()->GetDef(
|
||||
instruction->type_id());
|
||||
if (type_inst->opcode() != SpvOpTypePointer) {
|
||||
// Not a pointer.
|
||||
return false;
|
||||
}
|
||||
if (instruction->IsReadOnlyPointer()) {
|
||||
// Read only: cannot store to it.
|
||||
return false;
|
||||
}
|
||||
switch (instruction->opcode()) {
|
||||
case SpvOpConstantNull:
|
||||
case SpvOpUndef:
|
||||
// Do not allow storing to a null or undefined pointer;
|
||||
// this might be OK if the block is dead, but for now we
|
||||
// conservatively avoid it.
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->BlockIsDead(block->id()) ||
|
||||
GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->PointeeValueIsIrrelevant(
|
||||
instruction->result_id());
|
||||
});
|
||||
|
||||
// At this point, |relevant_pointers| contains all the pointers we might
|
||||
// think of storing to.
|
||||
if (relevant_pointers.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex(
|
||||
relevant_pointers)];
|
||||
|
||||
std::vector<opt::Instruction*> relevant_values =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[pointer](opt::IRContext* context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
return instruction->type_id() ==
|
||||
context->get_def_use_mgr()
|
||||
->GetDef(pointer->type_id())
|
||||
->GetSingleWordInOperand(1);
|
||||
});
|
||||
|
||||
if (relevant_values.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose a value at random, and create and apply a storing
|
||||
// transformation based on it and the pointer.
|
||||
ApplyTransformation(TransformationStore(
|
||||
pointer->result_id(),
|
||||
relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
|
||||
->result_id(),
|
||||
instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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_STORES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Fuzzer pass that adds stores, at random, through pointers in the module,
|
||||
// either (a) from dead blocks, or (b) through pointers whose pointee values
|
||||
// are known not to affect the module's overall behaviour.
|
||||
class FuzzerPassAddStores : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddStores(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddStores();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_add_synonyms.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
|
||||
|
||||
void FuzzerPassAddSynonyms::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
// Skip |inst_it| if we can't insert anything above it. OpIAdd is just
|
||||
// a representative of some instruction that might be produced by the
|
||||
// transformation.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingSynonyms())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto synonym_type = GetFuzzerContext()->GetRandomSynonymType();
|
||||
|
||||
// Select all instructions that can be used to create a synonym to.
|
||||
auto available_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[synonym_type, this](opt::IRContext* ir_context,
|
||||
opt::Instruction* inst) {
|
||||
// Check that we can create a synonym to |inst| as described by
|
||||
// the |synonym_type| and insert it before |inst_it|.
|
||||
return TransformationAddSynonym::IsInstructionValid(
|
||||
ir_context, *GetTransformationContext(), inst, synonym_type);
|
||||
});
|
||||
|
||||
if (available_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* existing_synonym =
|
||||
available_instructions[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions)];
|
||||
|
||||
// Make sure the module contains all instructions required to apply the
|
||||
// transformation.
|
||||
switch (synonym_type) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
// Create a zero constant to be used as an operand of the synonymous
|
||||
// instruction.
|
||||
FindOrCreateZeroConstant(existing_synonym->type_id(), false);
|
||||
break;
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
const auto* existing_synonym_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(
|
||||
existing_synonym->type_id());
|
||||
assert(existing_synonym_type && "Instruction has invalid type");
|
||||
|
||||
if (const auto* vector = existing_synonym_type->AsVector()) {
|
||||
auto element_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(vector->element_type());
|
||||
assert(element_type_id && "Vector's element type is invalid");
|
||||
|
||||
auto one_word = vector->element_type()->AsFloat()
|
||||
? fuzzerutil::FloatToWord(1)
|
||||
: 1u;
|
||||
FindOrCreateCompositeConstant(
|
||||
std::vector<uint32_t>(
|
||||
vector->element_count(),
|
||||
FindOrCreateConstant({one_word}, element_type_id, false)),
|
||||
existing_synonym->type_id(), false);
|
||||
} else {
|
||||
FindOrCreateConstant(
|
||||
{existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
|
||||
: 1u},
|
||||
existing_synonym->type_id(), false);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// This assertion will fail if some SynonymType is missing from the
|
||||
// switch statement.
|
||||
assert(
|
||||
!TransformationAddSynonym::IsAdditionalConstantRequired(
|
||||
synonym_type) &&
|
||||
"|synonym_type| requires an additional constant to be present "
|
||||
"in the module");
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationAddSynonym(
|
||||
existing_synonym->result_id(), synonym_type,
|
||||
GetFuzzerContext()->GetFreshId(), instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Sprinkles instructions through the module that produce ids, synonymous to
|
||||
// some other instructions.
|
||||
class FuzzerPassAddSynonyms : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddSynonyms(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddSynonyms() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
|
||||
@@ -1,145 +0,0 @@
|
||||
// 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_add_vector_shuffle_instructions.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_vector_shuffle.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddVectorShuffleInstructions::
|
||||
~FuzzerPassAddVectorShuffleInstructions() = default;
|
||||
|
||||
void FuzzerPassAddVectorShuffleInstructions::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator instruction_iterator,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(instruction_iterator->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Randomly decide whether to try adding an OpVectorShuffle instruction.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It must be valid to insert an OpVectorShuffle instruction
|
||||
// before |instruction_iterator|.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
|
||||
SpvOpVectorShuffle, instruction_iterator)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Looks for vectors that we might consider to use as OpVectorShuffle
|
||||
// operands.
|
||||
std::vector<opt::Instruction*> vector_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, instruction_iterator,
|
||||
[this, instruction_descriptor](
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ir_context->get_type_mgr()
|
||||
->GetType(instruction->type_id())
|
||||
->AsVector()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->IdIsIrrelevant(instruction->result_id()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context,
|
||||
*GetTransformationContext(),
|
||||
instruction)) {
|
||||
// If the id is irrelevant, we can use it since it will not
|
||||
// participate in DataSynonym fact. Otherwise, we should be
|
||||
// able to produce a synonym out of the id.
|
||||
return false;
|
||||
}
|
||||
|
||||
return fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
ir_context,
|
||||
FindInstruction(instruction_descriptor, ir_context),
|
||||
instruction->result_id());
|
||||
});
|
||||
|
||||
// If there are no vector instructions, then return.
|
||||
if (vector_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto vector_1_instruction =
|
||||
vector_instructions[GetFuzzerContext()->RandomIndex(
|
||||
vector_instructions)];
|
||||
auto vector_1_type = GetIRContext()
|
||||
->get_type_mgr()
|
||||
->GetType(vector_1_instruction->type_id())
|
||||
->AsVector();
|
||||
|
||||
auto vector_2_instruction =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
|
||||
auto vector_2_type = GetIRContext()
|
||||
->get_type_mgr()
|
||||
->GetType(vector_2_instruction->type_id())
|
||||
->AsVector();
|
||||
|
||||
// |vector_1| and |vector_2| must have the same element type as each
|
||||
// other. The loop is guaranteed to terminate because each iteration
|
||||
// removes on possible choice for |vector_2|, and there is at least one
|
||||
// choice that will cause the loop to exit - namely |vector_1|.
|
||||
while (vector_1_type->element_type() != vector_2_type->element_type()) {
|
||||
vector_2_instruction =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
|
||||
vector_2_type = GetIRContext()
|
||||
->get_type_mgr()
|
||||
->GetType(vector_2_instruction->type_id())
|
||||
->AsVector();
|
||||
}
|
||||
|
||||
// Gets components and creates the appropriate result vector type.
|
||||
std::vector<uint32_t> components =
|
||||
GetFuzzerContext()->GetRandomComponentsForVectorShuffle(
|
||||
vector_1_type->element_count() +
|
||||
vector_2_type->element_count());
|
||||
FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId(
|
||||
vector_1_type->element_type()),
|
||||
static_cast<uint32_t>(components.size()));
|
||||
|
||||
// Applies the vector shuffle transformation.
|
||||
ApplyTransformation(TransformationVectorShuffle(
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId(),
|
||||
vector_1_instruction->result_id(),
|
||||
vector_2_instruction->result_id(), components));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// 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_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Adds OpVectorShuffle instructions to the module.
|
||||
class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddVectorShuffleInstructions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddVectorShuffleInstructions();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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_adjust_branch_weights.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_adjust_branch_weights.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustBranchWeights::~FuzzerPassAdjustBranchWeights() = default;
|
||||
|
||||
void FuzzerPassAdjustBranchWeights::Apply() {
|
||||
// For all OpBranchConditional instructions,
|
||||
// randomly applies the transformation.
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
if (instruction->opcode() == SpvOpBranchConditional &&
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingBranchWeights())) {
|
||||
ApplyTransformation(TransformationAdjustBranchWeights(
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction),
|
||||
GetFuzzerContext()->GetRandomBranchWeights()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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_ADJUST_BRANCH_WEIGHTS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// This fuzzer pass searches for branch conditional instructions
|
||||
// and randomly chooses which of these instructions will have their weights
|
||||
// adjusted.
|
||||
class FuzzerPassAdjustBranchWeights : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustBranchWeights(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustBranchWeights();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (c) 2019 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_adjust_function_controls.h"
|
||||
|
||||
#include "source/fuzz/transformation_set_function_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
|
||||
|
||||
void FuzzerPassAdjustFunctionControls::Apply() {
|
||||
// Consider every function in the module.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
// Randomly decide whether to adjust this function's controls.
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) {
|
||||
// Grab the function control mask for the function in its present form.
|
||||
uint32_t existing_function_control_mask =
|
||||
function.DefInst().GetSingleWordInOperand(0);
|
||||
|
||||
// For the new mask, we first randomly select one of three basic masks:
|
||||
// None, Inline or DontInline. These are always valid (and are mutually
|
||||
// exclusive).
|
||||
std::vector<uint32_t> basic_function_control_masks = {
|
||||
SpvFunctionControlMaskNone, SpvFunctionControlInlineMask,
|
||||
SpvFunctionControlDontInlineMask};
|
||||
uint32_t new_function_control_mask =
|
||||
basic_function_control_masks[GetFuzzerContext()->RandomIndex(
|
||||
basic_function_control_masks)];
|
||||
|
||||
// We now consider the Pure and Const mask bits. If these are already
|
||||
// set on the function then it's OK to keep them, but also interesting
|
||||
// to consider dropping them, so we decide randomly in each case.
|
||||
for (auto mask_bit :
|
||||
{SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
|
||||
if ((existing_function_control_mask & mask_bit) &&
|
||||
GetFuzzerContext()->ChooseEven()) {
|
||||
new_function_control_mask |= mask_bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and add a transformation.
|
||||
TransformationSetFunctionControl transformation(
|
||||
function.DefInst().result_id(), new_function_control_mask);
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_ADJUST_FUNCTION_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that adjusts the function controls on OpFunction instructions.
|
||||
class FuzzerPassAdjustFunctionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustFunctionControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustFunctionControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_
|
||||
@@ -1,118 +0,0 @@
|
||||
// Copyright (c) 2019 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_adjust_loop_controls.h"
|
||||
|
||||
#include "source/fuzz/transformation_set_loop_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
|
||||
|
||||
void FuzzerPassAdjustLoopControls::Apply() {
|
||||
// Consider every merge instruction in the module (via looking through all
|
||||
// functions and blocks).
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
if (auto merge_inst = block.GetMergeInst()) {
|
||||
// Ignore the instruction if it is not a loop merge.
|
||||
if (merge_inst->opcode() != SpvOpLoopMerge) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decide randomly whether to adjust this loop merge.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t existing_mask = merge_inst->GetSingleWordOperand(
|
||||
TransformationSetLoopControl::kLoopControlMaskInOperandIndex);
|
||||
|
||||
// First, set the new mask to one of None, Unroll or DontUnroll.
|
||||
std::vector<uint32_t> basic_masks = {SpvLoopControlMaskNone,
|
||||
SpvLoopControlUnrollMask,
|
||||
SpvLoopControlDontUnrollMask};
|
||||
uint32_t new_mask =
|
||||
basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)];
|
||||
|
||||
// For the loop controls that depend on guarantees about what the loop
|
||||
// does, check which of these were present in the existing mask and
|
||||
// randomly decide whether to keep them. They are just hints, so
|
||||
// removing them should not change the semantics of the module.
|
||||
for (auto mask_bit :
|
||||
{SpvLoopControlDependencyInfiniteMask,
|
||||
SpvLoopControlDependencyLengthMask,
|
||||
SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask,
|
||||
SpvLoopControlIterationMultipleMask}) {
|
||||
if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) {
|
||||
// The mask bits we are considering are not available in all SPIR-V
|
||||
// versions. However, we only include a mask bit if it was present
|
||||
// in the original loop control mask, and we work under the
|
||||
// assumption that we are transforming a valid module, thus we don't
|
||||
// need to actually check whether the SPIR-V version being used
|
||||
// supports these loop control mask bits.
|
||||
new_mask |= mask_bit;
|
||||
}
|
||||
}
|
||||
|
||||
// We use 0 for peel count and partial count in the case that we choose
|
||||
// not to set these controls.
|
||||
uint32_t peel_count = 0;
|
||||
uint32_t partial_count = 0;
|
||||
|
||||
// PeelCount and PartialCount are not compatible with DontUnroll, so
|
||||
// we check whether DontUnroll is set.
|
||||
if (!(new_mask & SpvLoopControlDontUnrollMask)) {
|
||||
// If PeelCount is supported by this SPIR-V version, randomly choose
|
||||
// whether to set it. If it was set in the original mask and is not
|
||||
// selected for setting here, that amounts to dropping it.
|
||||
if (TransformationSetLoopControl::PeelCountIsSupported(
|
||||
GetIRContext()) &&
|
||||
GetFuzzerContext()->ChooseEven()) {
|
||||
new_mask |= SpvLoopControlPeelCountMask;
|
||||
// The peel count is chosen randomly - if PeelCount was already set
|
||||
// this will overwrite whatever peel count was previously used.
|
||||
peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount();
|
||||
}
|
||||
// Similar, but for PartialCount.
|
||||
if (TransformationSetLoopControl::PartialCountIsSupported(
|
||||
GetIRContext()) &&
|
||||
GetFuzzerContext()->ChooseEven()) {
|
||||
new_mask |= SpvLoopControlPartialCountMask;
|
||||
partial_count =
|
||||
GetFuzzerContext()->GetRandomLoopControlPartialCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the transformation and add it to the output transformation
|
||||
// sequence.
|
||||
TransformationSetLoopControl transformation(block.id(), new_mask,
|
||||
peel_count, partial_count);
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_ADJUST_LOOP_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that adjusts the loop controls on OpLoopMerge instructions.
|
||||
class FuzzerPassAdjustLoopControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustLoopControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustLoopControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
|
||||
@@ -1,109 +0,0 @@
|
||||
// Copyright (c) 2019 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_adjust_memory_operands_masks.h"
|
||||
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
|
||||
default;
|
||||
|
||||
void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
|
||||
// Consider every block in every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// Consider every instruction in this block, using an explicit iterator so
|
||||
// that when we find an instruction of interest we can search backwards to
|
||||
// create an id descriptor for it.
|
||||
for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
|
||||
if (!TransformationSetMemoryOperandsMask::IsMemoryAccess(*inst_it)) {
|
||||
// We are only interested in memory access instructions.
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indices_of_available_masks_to_adjust;
|
||||
// All memory instructions have at least one memory operands mask.
|
||||
indices_of_available_masks_to_adjust.push_back(0);
|
||||
// From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a
|
||||
// second mask.
|
||||
switch (inst_it->opcode()) {
|
||||
case SpvOpCopyMemory:
|
||||
case SpvOpCopyMemorySized:
|
||||
if (TransformationSetMemoryOperandsMask::
|
||||
MultipleMemoryOperandMasksAreSupported(GetIRContext())) {
|
||||
indices_of_available_masks_to_adjust.push_back(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Consider the available masks
|
||||
for (auto mask_index : indices_of_available_masks_to_adjust) {
|
||||
// Randomly decide whether to adjust this mask.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfAdjustingMemoryOperandsMask())) {
|
||||
continue;
|
||||
}
|
||||
// Get the existing mask, using None if there was no mask present at
|
||||
// all.
|
||||
auto existing_mask_in_operand_index =
|
||||
TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
|
||||
*inst_it, mask_index);
|
||||
auto existing_mask =
|
||||
existing_mask_in_operand_index < inst_it->NumInOperands()
|
||||
? inst_it->GetSingleWordInOperand(
|
||||
existing_mask_in_operand_index)
|
||||
: static_cast<uint32_t>(SpvMemoryAccessMaskNone);
|
||||
|
||||
// There are two things we can do to a mask:
|
||||
// - add Volatile if not already present
|
||||
// - toggle Nontemporal
|
||||
// The following ensures that we do at least one of these
|
||||
bool add_volatile = !(existing_mask & SpvMemoryAccessVolatileMask) &&
|
||||
GetFuzzerContext()->ChooseEven();
|
||||
bool toggle_nontemporal =
|
||||
!add_volatile || GetFuzzerContext()->ChooseEven();
|
||||
|
||||
// These bitwise operations use '|' to add Volatile if desired, and
|
||||
// '^' to toggle Nontemporal if desired.
|
||||
uint32_t new_mask =
|
||||
(existing_mask | (add_volatile ? SpvMemoryAccessVolatileMask
|
||||
: SpvMemoryAccessMaskNone)) ^
|
||||
(toggle_nontemporal ? SpvMemoryAccessNontemporalMask
|
||||
: SpvMemoryAccessMaskNone);
|
||||
|
||||
TransformationSetMemoryOperandsMask transformation(
|
||||
MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2019 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_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass to adjust the memory operand masks in memory access
|
||||
// instructions.
|
||||
class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustMemoryOperandsMasks(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustMemoryOperandsMasks();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2019 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_adjust_selection_controls.h"
|
||||
|
||||
#include "source/fuzz/transformation_set_selection_control.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
|
||||
default;
|
||||
|
||||
void FuzzerPassAdjustSelectionControls::Apply() {
|
||||
// Consider every merge instruction in the module (via looking through all
|
||||
// functions and blocks).
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
if (auto merge_inst = block.GetMergeInst()) {
|
||||
// Ignore the instruction if it is not a selection merge.
|
||||
if (merge_inst->opcode() != SpvOpSelectionMerge) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Choose randomly whether to change the selection control for this
|
||||
// instruction.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The choices to change the selection control to are the set of valid
|
||||
// controls, minus the current control.
|
||||
std::vector<uint32_t> choices;
|
||||
for (auto control :
|
||||
{SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask,
|
||||
SpvSelectionControlDontFlattenMask}) {
|
||||
if (control == merge_inst->GetSingleWordOperand(1)) {
|
||||
continue;
|
||||
}
|
||||
choices.push_back(control);
|
||||
}
|
||||
|
||||
// Apply the transformation and add it to the output transformation
|
||||
// sequence.
|
||||
TransformationSetSelectionControl transformation(
|
||||
block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
|
||||
ApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_ADJUST_SELECTION_CONTROLS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that adjusts the selection controls on OpSelectionMerge instructions.
|
||||
class FuzzerPassAdjustSelectionControls : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAdjustSelectionControls(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAdjustSelectionControls() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright (c) 2019 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_apply_id_synonyms.h"
|
||||
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_composite_extract.h"
|
||||
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
|
||||
|
||||
void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
// Compute a closure of data synonym facts, to enrich the pool of synonyms
|
||||
// that are available.
|
||||
ApplyTransformation(TransformationComputeDataSynonymFactClosure(
|
||||
GetFuzzerContext()
|
||||
->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure()));
|
||||
|
||||
for (auto id_with_known_synonyms : GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->GetIdsForWhichSynonymsAreKnown()) {
|
||||
// Gather up all uses of |id_with_known_synonym| as a regular id, and
|
||||
// subsequently iterate over these uses. We use this separation because,
|
||||
// when considering a given use, we might apply a transformation that will
|
||||
// invalidate the def-use manager.
|
||||
std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
id_with_known_synonyms,
|
||||
[&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
|
||||
// We only gather up regular id uses; e.g. we do not include a use of
|
||||
// the id as the scope for an atomic operation.
|
||||
if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
|
||||
uses.emplace_back(
|
||||
std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
|
||||
}
|
||||
});
|
||||
|
||||
for (const auto& use : uses) {
|
||||
auto use_inst = use.first;
|
||||
auto use_index = use.second;
|
||||
auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
|
||||
// The use might not be in a block; e.g. it could be a decoration.
|
||||
if (!block_containing_use) {
|
||||
continue;
|
||||
}
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) {
|
||||
continue;
|
||||
}
|
||||
// |use_index| is the absolute index of the operand. We require
|
||||
// the index of the operand restricted to input operands only.
|
||||
uint32_t use_in_operand_index =
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
|
||||
GetIRContext(), use_inst, use_in_operand_index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
|
||||
for (const auto* data_descriptor :
|
||||
GetTransformationContext()->GetFactManager()->GetSynonymsForId(
|
||||
id_with_known_synonyms)) {
|
||||
protobufs::DataDescriptor descriptor_for_this_id =
|
||||
MakeDataDescriptor(id_with_known_synonyms, {});
|
||||
if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
|
||||
// Exclude the fact that the id is synonymous with itself.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DataDescriptorsHaveCompatibleTypes(
|
||||
use_inst->opcode(), use_in_operand_index,
|
||||
descriptor_for_this_id, *data_descriptor)) {
|
||||
synonyms_to_try.push_back(data_descriptor);
|
||||
}
|
||||
}
|
||||
while (!synonyms_to_try.empty()) {
|
||||
auto synonym_to_try =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
|
||||
|
||||
// If the synonym's |index_size| is zero, the synonym represents an id.
|
||||
// Otherwise it represents some element of a composite structure, in
|
||||
// which case we need to be able to add an extract instruction to get
|
||||
// that element out.
|
||||
if (synonym_to_try->index_size() > 0 &&
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
|
||||
use_inst) &&
|
||||
use_inst->opcode() != SpvOpPhi) {
|
||||
// We cannot insert an extract before this instruction, so this
|
||||
// synonym is no good.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
|
||||
use_in_operand_index,
|
||||
synonym_to_try->object())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We either replace the use with an id known to be synonymous (when
|
||||
// the synonym's |index_size| is 0), or an id that will hold the result
|
||||
// of extracting a synonym from a composite (when the synonym's
|
||||
// |index_size| is > 0).
|
||||
uint32_t id_with_which_to_replace_use;
|
||||
if (synonym_to_try->index_size() == 0) {
|
||||
id_with_which_to_replace_use = synonym_to_try->object();
|
||||
} else {
|
||||
id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
|
||||
opt::Instruction* instruction_to_insert_before = nullptr;
|
||||
|
||||
if (use_inst->opcode() != SpvOpPhi) {
|
||||
instruction_to_insert_before = use_inst;
|
||||
} else {
|
||||
auto parent_block_id =
|
||||
use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
|
||||
auto parent_block_instruction =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
|
||||
auto parent_block =
|
||||
GetIRContext()->get_instr_block(parent_block_instruction);
|
||||
|
||||
instruction_to_insert_before = parent_block->GetMergeInst()
|
||||
? parent_block->GetMergeInst()
|
||||
: parent_block->terminator();
|
||||
}
|
||||
|
||||
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
synonym_to_try->object()) &&
|
||||
"Irrelevant ids can't participate in DataSynonym facts");
|
||||
ApplyTransformation(TransformationCompositeExtract(
|
||||
MakeInstructionDescriptor(GetIRContext(),
|
||||
instruction_to_insert_before),
|
||||
id_with_which_to_replace_use, synonym_to_try->object(),
|
||||
fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
|
||||
use_in_operand_index),
|
||||
id_with_which_to_replace_use));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
|
||||
SpvOp opcode, uint32_t use_in_operand_index,
|
||||
const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
auto base_object_type_id_1 =
|
||||
fuzzerutil::GetTypeId(GetIRContext(), dd1.object());
|
||||
auto base_object_type_id_2 =
|
||||
fuzzerutil::GetTypeId(GetIRContext(), dd2.object());
|
||||
assert(base_object_type_id_1 && base_object_type_id_2 &&
|
||||
"Data descriptors are invalid");
|
||||
|
||||
auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
GetIRContext(), base_object_type_id_1, dd1.index());
|
||||
auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
GetIRContext(), base_object_type_id_2, dd2.index());
|
||||
assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
|
||||
|
||||
return TransformationReplaceIdWithSynonym::TypesAreCompatible(
|
||||
GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2019 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_APPLY_ID_SYNONYMS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that replaces ids with other ids, or accesses into structures, that
|
||||
// are known to hold the same values.
|
||||
class FuzzerPassApplyIdSynonyms : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassApplyIdSynonyms() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Returns true if uses of |dd1| can be replaced with |dd2| and vice-versa
|
||||
// with respect to the type. Concretely, returns true if |dd1| and |dd2| have
|
||||
// the same type or both |dd1| and |dd2| are either a numerical or a vector
|
||||
// type of integral components with possibly different signedness.
|
||||
bool DataDescriptorsHaveCompatibleTypes(SpvOp opcode,
|
||||
uint32_t use_in_operand_index,
|
||||
const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
|
||||
@@ -1,385 +0,0 @@
|
||||
// Copyright (c) 2019 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_construct_composites.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_composite_construct.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassConstructComposites::FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
|
||||
|
||||
void FuzzerPassConstructComposites::Apply() {
|
||||
// Gather up the ids of all composite types.
|
||||
std::vector<uint32_t> composite_type_ids;
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
if (fuzzerutil::IsCompositeType(
|
||||
GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) {
|
||||
composite_type_ids.push_back(inst.result_id());
|
||||
}
|
||||
}
|
||||
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this, &composite_type_ids](
|
||||
opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
// Check whether it is legitimate to insert a composite construction
|
||||
// before the instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
|
||||
SpvOpCompositeConstruct, inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting an object copy here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfConstructingComposite())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each instruction that is available at this program point (i.e. an
|
||||
// instruction that is global or whose definition strictly dominates the
|
||||
// program point) and suitable for making a synonym of, associate it
|
||||
// with the id of its result type.
|
||||
TypeIdToInstructions type_id_to_available_instructions;
|
||||
auto available_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
if (!inst->result_id() || !inst->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the id is irrelevant, we can use it since it will not
|
||||
// participate in DataSynonym fact. Otherwise, we should be able
|
||||
// to produce a synonym out of the id.
|
||||
return GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->IdIsIrrelevant(inst->result_id()) ||
|
||||
fuzzerutil::CanMakeSynonymOf(
|
||||
ir_context, *GetTransformationContext(), inst);
|
||||
});
|
||||
for (auto instruction : available_instructions) {
|
||||
RecordAvailableInstruction(instruction,
|
||||
&type_id_to_available_instructions);
|
||||
}
|
||||
|
||||
// At this point, |composite_type_ids| captures all the composite types
|
||||
// we could try to create, while |type_id_to_available_instructions|
|
||||
// captures all the available result ids we might use, organized by
|
||||
// type.
|
||||
|
||||
// Now we try to find a composite that we can construct. We might not
|
||||
// manage, if there is a paucity of available ingredients in the module
|
||||
// (e.g. if our only available composite was a boolean vector and we had
|
||||
// no instructions generating boolean result types available).
|
||||
//
|
||||
// If we succeed, |chosen_composite_type| will end up being non-zero,
|
||||
// and |constructor_arguments| will end up giving us result ids suitable
|
||||
// for constructing a composite of that type. Otherwise these variables
|
||||
// will remain 0 and null respectively.
|
||||
uint32_t chosen_composite_type = 0;
|
||||
std::vector<uint32_t> constructor_arguments;
|
||||
|
||||
// Initially, all composite type ids are available for us to try. Keep
|
||||
// trying until we run out of options.
|
||||
auto composites_to_try_constructing = composite_type_ids;
|
||||
while (!composites_to_try_constructing.empty()) {
|
||||
// Remove a composite type from the composite types left for us to
|
||||
// try.
|
||||
auto next_composite_to_try_constructing =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(
|
||||
&composites_to_try_constructing);
|
||||
|
||||
// Now try to construct a composite of this type, using an appropriate
|
||||
// helper method depending on the kind of composite type.
|
||||
auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
next_composite_to_try_constructing);
|
||||
switch (composite_type_inst->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
constructor_arguments = FindComponentsToConstructArray(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
constructor_arguments = FindComponentsToConstructMatrix(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeStruct:
|
||||
constructor_arguments = FindComponentsToConstructStruct(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
constructor_arguments = FindComponentsToConstructVector(
|
||||
*composite_type_inst, type_id_to_available_instructions);
|
||||
break;
|
||||
default:
|
||||
assert(false &&
|
||||
"The space of possible composite types should be covered "
|
||||
"by the above cases.");
|
||||
break;
|
||||
}
|
||||
if (!constructor_arguments.empty()) {
|
||||
// We succeeded! Note the composite type we finally settled on, and
|
||||
// exit from the loop.
|
||||
chosen_composite_type = next_composite_to_try_constructing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!chosen_composite_type) {
|
||||
// We did not manage to make a composite; return 0 to indicate that no
|
||||
// instructions were added.
|
||||
assert(constructor_arguments.empty());
|
||||
return;
|
||||
}
|
||||
assert(!constructor_arguments.empty());
|
||||
|
||||
// Make and apply a transformation.
|
||||
ApplyTransformation(TransformationCompositeConstruct(
|
||||
chosen_composite_type, constructor_arguments,
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId()));
|
||||
});
|
||||
}
|
||||
|
||||
void FuzzerPassConstructComposites::RecordAvailableInstruction(
|
||||
opt::Instruction* inst,
|
||||
TypeIdToInstructions* type_id_to_available_instructions) {
|
||||
if (type_id_to_available_instructions->count(inst->type_id()) == 0) {
|
||||
(*type_id_to_available_instructions)[inst->type_id()] = {};
|
||||
}
|
||||
type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructArray(
|
||||
const opt::Instruction& array_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
assert(array_type_instruction.opcode() == SpvOpTypeArray &&
|
||||
"Precondition: instruction must be an array type.");
|
||||
|
||||
// Get the element type for the array.
|
||||
auto element_type_id = array_type_instruction.GetSingleWordInOperand(0);
|
||||
|
||||
// Get all instructions at our disposal that compute something of this element
|
||||
// type.
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(element_type_id);
|
||||
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// If there are not any instructions available that compute the element type
|
||||
// of the array then we are not in a position to construct a composite with
|
||||
// this array type.
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t array_length =
|
||||
GetIRContext()
|
||||
->get_def_use_mgr()
|
||||
->GetDef(array_type_instruction.GetSingleWordInOperand(1))
|
||||
->GetSingleWordInOperand(0);
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t index = 0; index < array_length; index++) {
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructMatrix(
|
||||
const opt::Instruction& matrix_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix &&
|
||||
"Precondition: instruction must be a matrix type.");
|
||||
|
||||
// Get the element type for the matrix.
|
||||
auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0);
|
||||
|
||||
// Get all instructions at our disposal that compute something of this element
|
||||
// type.
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(element_type_id);
|
||||
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// If there are not any instructions available that compute the element type
|
||||
// of the matrix then we are not in a position to construct a composite with
|
||||
// this matrix type.
|
||||
return {};
|
||||
}
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t index = 0;
|
||||
index < matrix_type_instruction.GetSingleWordInOperand(1); index++) {
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructStruct(
|
||||
const opt::Instruction& struct_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
|
||||
"Precondition: instruction must be a struct type.");
|
||||
std::vector<uint32_t> result;
|
||||
// Consider the type of each field of the struct.
|
||||
for (uint32_t in_operand_index = 0;
|
||||
in_operand_index < struct_type_instruction.NumInOperands();
|
||||
in_operand_index++) {
|
||||
auto element_type_id =
|
||||
struct_type_instruction.GetSingleWordInOperand(in_operand_index);
|
||||
// Find the instructions at our disposal that compute something of the field
|
||||
// type.
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(element_type_id);
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
// If there are no such instructions, we cannot construct a composite of
|
||||
// this struct type.
|
||||
return {};
|
||||
}
|
||||
result.push_back(available_instructions
|
||||
->second[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions->second)]
|
||||
->result_id());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint32_t>
|
||||
FuzzerPassConstructComposites::FindComponentsToConstructVector(
|
||||
const opt::Instruction& vector_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions) {
|
||||
assert(vector_type_instruction.opcode() == SpvOpTypeVector &&
|
||||
"Precondition: instruction must be a vector type.");
|
||||
|
||||
// Get details of the type underlying the vector, and the width of the vector,
|
||||
// for convenience.
|
||||
auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0);
|
||||
auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
|
||||
auto element_count = vector_type_instruction.GetSingleWordInOperand(1);
|
||||
|
||||
// Collect a mapping, from type id to width, for scalar/vector types that are
|
||||
// smaller in width than |vector_type|, but that have the same underlying
|
||||
// type. For example, if |vector_type| is vec4, the mapping will be:
|
||||
// { float -> 1, vec2 -> 2, vec3 -> 3 }
|
||||
// The mapping will have missing entries if some of these types do not exist.
|
||||
|
||||
std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
|
||||
// Add the underlying type. This id must exist, in order for |vector_type| to
|
||||
// exist.
|
||||
smaller_vector_type_id_to_width[element_type_id] = 1;
|
||||
|
||||
// Now add every vector type with width at least 2, and less than the width of
|
||||
// |vector_type|.
|
||||
for (uint32_t width = 2; width < element_count; width++) {
|
||||
opt::analysis::Vector smaller_vector_type(element_type, width);
|
||||
auto smaller_vector_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
|
||||
// We might find that there is no declared type of this smaller width.
|
||||
// For example, a module can declare vec4 without having declared vec2 or
|
||||
// vec3.
|
||||
if (smaller_vector_type_id) {
|
||||
smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we know the types that are available to us, we set about populating a
|
||||
// vector of the right length. We do this by deciding, with no order in mind,
|
||||
// which instructions we will use to populate the vector, and subsequently
|
||||
// randomly choosing an order. This is to avoid biasing construction of
|
||||
// vectors with smaller vectors to the left and scalars to the right. That is
|
||||
// a concern because, e.g. in the case of populating a vec4, if we populate
|
||||
// the constructor instructions left-to-right, we can always choose a vec3 to
|
||||
// construct the first three elements, but can only choose a vec3 to construct
|
||||
// the last three elements if we chose a float to construct the first element
|
||||
// (otherwise there will not be space left for a vec3).
|
||||
|
||||
uint32_t vector_slots_used = 0;
|
||||
// The instructions we will use to construct the vector, in no particular
|
||||
// order at this stage.
|
||||
std::vector<opt::Instruction*> instructions_to_use;
|
||||
|
||||
while (vector_slots_used < element_count) {
|
||||
std::vector<opt::Instruction*> instructions_to_choose_from;
|
||||
for (auto& entry : smaller_vector_type_id_to_width) {
|
||||
if (entry.second >
|
||||
std::min(element_count - 1, element_count - vector_slots_used)) {
|
||||
continue;
|
||||
}
|
||||
auto available_instructions =
|
||||
type_id_to_available_instructions.find(entry.first);
|
||||
if (available_instructions == type_id_to_available_instructions.cend()) {
|
||||
continue;
|
||||
}
|
||||
instructions_to_choose_from.insert(instructions_to_choose_from.end(),
|
||||
available_instructions->second.begin(),
|
||||
available_instructions->second.end());
|
||||
}
|
||||
if (instructions_to_choose_from.empty()) {
|
||||
// We may get unlucky and find that there are not any instructions to
|
||||
// choose from. In this case we give up constructing a composite of this
|
||||
// vector type. It might be that we could construct the composite in
|
||||
// another manner, so we could opt to retry a few times here, but it is
|
||||
// simpler to just give up on the basis that this will not happen
|
||||
// frequently.
|
||||
return {};
|
||||
}
|
||||
auto instruction_to_use =
|
||||
instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
|
||||
instructions_to_choose_from)];
|
||||
instructions_to_use.push_back(instruction_to_use);
|
||||
auto chosen_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
|
||||
if (chosen_type->AsVector()) {
|
||||
assert(chosen_type->AsVector()->element_type() == element_type);
|
||||
assert(chosen_type->AsVector()->element_count() < element_count);
|
||||
assert(chosen_type->AsVector()->element_count() <=
|
||||
element_count - vector_slots_used);
|
||||
vector_slots_used += chosen_type->AsVector()->element_count();
|
||||
} else {
|
||||
assert(chosen_type == element_type);
|
||||
vector_slots_used += 1;
|
||||
}
|
||||
}
|
||||
assert(vector_slots_used == element_count);
|
||||
|
||||
std::vector<uint32_t> result;
|
||||
std::vector<uint32_t> operands;
|
||||
while (!instructions_to_use.empty()) {
|
||||
auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
|
||||
result.push_back(instructions_to_use[index]->result_id());
|
||||
instructions_to_use.erase(instructions_to_use.begin() + index);
|
||||
}
|
||||
assert(result.size() > 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) 2019 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_CONSTRUCT_COMPOSITES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass for constructing composite objects from smaller objects.
|
||||
class FuzzerPassConstructComposites : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassConstructComposites(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassConstructComposites();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Used to map a type id to relevant instructions whose result type matches
|
||||
// the type id.
|
||||
typedef std::map<uint32_t, std::vector<opt::Instruction*>>
|
||||
TypeIdToInstructions;
|
||||
|
||||
// Considers all instructions that are available at |inst| - instructions
|
||||
// whose results could be packed into a composite - and updates
|
||||
// |type_id_to_available_instructions| so that each such instruction is
|
||||
// associated with its the id of its result type.
|
||||
void RecordAvailableInstruction(
|
||||
opt::Instruction* inst,
|
||||
TypeIdToInstructions* type_id_to_available_instructions);
|
||||
|
||||
// Requires that |array_type_instruction| has opcode OpTypeArray.
|
||||
// Attempts to find suitable instruction result ids from the values of
|
||||
// |type_id_to_available_instructions| that would allow a composite of type
|
||||
// |array_type_instruction| to be constructed. Returns said ids if they can
|
||||
// be found and an empty vector otherwise.
|
||||
std::vector<uint32_t> FindComponentsToConstructArray(
|
||||
const opt::Instruction& array_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to FindComponentsToConstructArray, but for matrices.
|
||||
std::vector<uint32_t> FindComponentsToConstructMatrix(
|
||||
const opt::Instruction& matrix_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to FindComponentsToConstructArray, but for structs.
|
||||
std::vector<uint32_t> FindComponentsToConstructStruct(
|
||||
const opt::Instruction& struct_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
|
||||
// Similar to FindComponentsToConstructArray, but for vectors.
|
||||
std::vector<uint32_t> FindComponentsToConstructVector(
|
||||
const opt::Instruction& vector_type_instruction,
|
||||
const TypeIdToInstructions& type_id_to_available_instructions);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) 2019 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_copy_objects.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassCopyObjects::FuzzerPassCopyObjects(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
|
||||
|
||||
void FuzzerPassCopyObjects::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor)
|
||||
-> void {
|
||||
assert(inst_it->opcode() ==
|
||||
instruction_descriptor.target_instruction_opcode() &&
|
||||
"The opcode of the instruction we might insert before must be "
|
||||
"the same as the opcode in the descriptor for the instruction");
|
||||
|
||||
// Check whether it is legitimate to insert a copy before this
|
||||
// instruction.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
|
||||
inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decide whether to try inserting an object copy here.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfCopyingObject())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto relevant_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
return TransformationAddSynonym::IsInstructionValid(
|
||||
ir_context, *GetTransformationContext(), inst,
|
||||
protobufs::TransformationAddSynonym::COPY_OBJECT);
|
||||
});
|
||||
|
||||
// At this point, |relevant_instructions| contains all the instructions
|
||||
// we might think of copying.
|
||||
if (relevant_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose a copyable instruction at random, and create and apply an
|
||||
// object copying transformation based on it.
|
||||
ApplyTransformation(TransformationAddSynonym(
|
||||
relevant_instructions[GetFuzzerContext()->RandomIndex(
|
||||
relevant_instructions)]
|
||||
->result_id(),
|
||||
protobufs::TransformationAddSynonym::COPY_OBJECT,
|
||||
GetFuzzerContext()->GetFreshId(), instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2019 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_COPY_OBJECTS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass for adding adding copies of objects to the module.
|
||||
class FuzzerPassCopyObjects : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassCopyObjects(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassCopyObjects();
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,170 +0,0 @@
|
||||
// Copyright (c) 2019 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_DONATE_MODULES_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass that randomly adds code from other SPIR-V modules to the module
|
||||
// being transformed.
|
||||
class FuzzerPassDonateModules : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassDonateModules(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations,
|
||||
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers);
|
||||
|
||||
~FuzzerPassDonateModules();
|
||||
|
||||
void Apply() override;
|
||||
|
||||
// Donates the global declarations and functions of |donor_ir_context| into
|
||||
// the fuzzer pass's IR context. |make_livesafe| dictates whether the
|
||||
// functions of the donated module will be made livesafe (see
|
||||
// FactFunctionIsLivesafe).
|
||||
void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe);
|
||||
|
||||
private:
|
||||
// Adapts a storage class coming from a donor module so that it will work
|
||||
// in a recipient module, e.g. by changing Uniform to Private.
|
||||
static SpvStorageClass AdaptStorageClass(SpvStorageClass donor_storage_class);
|
||||
|
||||
// Identifies all external instruction set imports in |donor_ir_context| and
|
||||
// populates |original_id_to_donated_id| with a mapping from the donor's id
|
||||
// for such an import to a corresponding import in the recipient. Aborts if
|
||||
// no such corresponding import is available.
|
||||
void HandleExternalInstructionImports(
|
||||
opt::IRContext* donor_ir_context,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id);
|
||||
|
||||
// Considers all types, globals, constants and undefs in |donor_ir_context|.
|
||||
// For each instruction, uses |original_to_donated_id| to map its result id to
|
||||
// either (1) the id of an existing identical instruction in the recipient, or
|
||||
// (2) to a fresh id, in which case the instruction is also added to the
|
||||
// recipient (with any operand ids that it uses being remapped via
|
||||
// |original_id_to_donated_id|).
|
||||
void HandleTypesAndValues(
|
||||
opt::IRContext* donor_ir_context,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id);
|
||||
|
||||
// Helper method for HandleTypesAndValues, to handle a single type/value.
|
||||
void HandleTypeOrValue(
|
||||
const opt::Instruction& type_or_value,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id);
|
||||
|
||||
// Assumes that |donor_ir_context| does not exhibit recursion. Considers the
|
||||
// functions in |donor_ir_context|'s call graph in a reverse-topologically-
|
||||
// sorted order (leaves-to-root), adding each function to the recipient
|
||||
// module, rewritten to use fresh ids and using |original_id_to_donated_id| to
|
||||
// remap ids. The |make_livesafe| argument captures whether the functions in
|
||||
// the module are required to be made livesafe before being added to the
|
||||
// recipient.
|
||||
void HandleFunctions(opt::IRContext* donor_ir_context,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id,
|
||||
bool make_livesafe);
|
||||
|
||||
// During donation we will have to ignore some instructions, e.g. because they
|
||||
// use opcodes that we cannot support or because they reference the ids of
|
||||
// instructions that have not been donated. This function encapsulates the
|
||||
// logic for deciding which whether instruction |instruction| from
|
||||
// |donor_ir_context| can be donated.
|
||||
bool CanDonateInstruction(
|
||||
opt::IRContext* donor_ir_context, const opt::Instruction& instruction,
|
||||
const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
|
||||
const std::set<uint32_t>& skipped_instructions) const;
|
||||
|
||||
// We treat the OpArrayLength instruction specially. In the donor shader this
|
||||
// instruction yields the length of a runtime array that is the final member
|
||||
// of a struct. During donation, we will have converted the runtime array
|
||||
// type, and the associated struct field, into a fixed-size array.
|
||||
//
|
||||
// Instead of donating this instruction, we turn it into an OpCopyObject
|
||||
// instruction that copies the size of the fixed-size array.
|
||||
void HandleOpArrayLength(
|
||||
const opt::Instruction& instruction,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id,
|
||||
std::vector<protobufs::Instruction>* donated_instructions) const;
|
||||
|
||||
// The instruction |instruction| is required to be an instruction that cannot
|
||||
// be easily donated, either because it uses an unsupported opcode, has an
|
||||
// unsupported result type, or uses id operands that could not be donated.
|
||||
//
|
||||
// If |instruction| generates a result id, the function attempts to add a
|
||||
// substitute for |instruction| to |donated_instructions| that has the correct
|
||||
// result type. If this cannot be done, the instruction's result id is added
|
||||
// to |skipped_instructions|. The mapping from donor ids to recipient ids is
|
||||
// managed by |original_id_to_donated_id|.
|
||||
void HandleDifficultInstruction(
|
||||
const opt::Instruction& instruction,
|
||||
std::map<uint32_t, uint32_t>* original_id_to_donated_id,
|
||||
std::vector<protobufs::Instruction>* donated_instructions,
|
||||
std::set<uint32_t>* skipped_instructions);
|
||||
|
||||
// Adds an instruction based in |instruction| to |donated_instructions| in a
|
||||
// form ready for donation. The original instruction comes from
|
||||
// |donor_ir_context|, and |original_id_to_donated_id| maps ids from
|
||||
// |donor_ir_context| to corresponding ids in the recipient module.
|
||||
void PrepareInstructionForDonation(
|
||||
const opt::Instruction& instruction, opt::IRContext* donor_ir_context,
|
||||
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.
|
||||
//
|
||||
// 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);
|
||||
|
||||
// Returns true if and only if |instruction| is a scalar, vector, matrix,
|
||||
// array or struct; i.e. it is not an opaque type.
|
||||
bool IsBasicType(const opt::Instruction& instruction) const;
|
||||
|
||||
// Returns the ids of all functions in |context| in a topological order in
|
||||
// relation to the call graph of |context|, which is assumed to be recursion-
|
||||
// free.
|
||||
static std::vector<uint32_t> GetFunctionsInCallGraphTopologicalOrder(
|
||||
opt::IRContext* context);
|
||||
|
||||
// Functions that supply SPIR-V modules
|
||||
std::vector<fuzzerutil::ModuleSupplier> donor_suppliers_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_
|
||||
@@ -1,104 +0,0 @@
|
||||
// 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_inline_functions.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_inline_function.h"
|
||||
#include "source/fuzz/transformation_split_block.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassInlineFunctions::FuzzerPassInlineFunctions(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInlineFunctions::~FuzzerPassInlineFunctions() = default;
|
||||
|
||||
void FuzzerPassInlineFunctions::Apply() {
|
||||
// |function_call_instructions| are the instructions that will be inlined.
|
||||
// First, they will be collected and then do the inlining in another loop.
|
||||
// This avoids changing the module while it is being inspected.
|
||||
std::vector<opt::Instruction*> function_call_instructions;
|
||||
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
for (auto& instruction : block) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfInliningFunction())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// |instruction| must be suitable for inlining.
|
||||
if (!TransformationInlineFunction::IsSuitableForInlining(
|
||||
GetIRContext(), &instruction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
function_call_instructions.push_back(&instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once the function calls have been collected, it's time to actually create
|
||||
// and apply the inlining transformations.
|
||||
for (auto& function_call_instruction : function_call_instructions) {
|
||||
// If |function_call_instruction| is not the penultimate instruction in its
|
||||
// block or its block termination instruction is not OpBranch, then try to
|
||||
// split |function_call_block| such that the conditions are met.
|
||||
auto* function_call_block =
|
||||
GetIRContext()->get_instr_block(function_call_instruction);
|
||||
if ((function_call_instruction != &*--function_call_block->tail() ||
|
||||
function_call_block->terminator()->opcode() != SpvOpBranch) &&
|
||||
!MaybeApplyTransformation(TransformationSplitBlock(
|
||||
MakeInstructionDescriptor(GetIRContext(),
|
||||
function_call_instruction->NextNode()),
|
||||
GetFuzzerContext()->GetFreshId()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* called_function = fuzzerutil::FindFunction(
|
||||
GetIRContext(), function_call_instruction->GetSingleWordInOperand(0));
|
||||
|
||||
// Mapping the called function instructions.
|
||||
std::map<uint32_t, uint32_t> result_id_map;
|
||||
for (auto& called_function_block : *called_function) {
|
||||
// The called function entry block label will not be inlined.
|
||||
if (&called_function_block != &*called_function->entry()) {
|
||||
result_id_map[called_function_block.GetLabelInst()->result_id()] =
|
||||
GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
|
||||
for (auto& instruction_to_inline : called_function_block) {
|
||||
// The instructions are mapped to fresh ids.
|
||||
if (instruction_to_inline.HasResultId()) {
|
||||
result_id_map[instruction_to_inline.result_id()] =
|
||||
GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Applies the inline function transformation.
|
||||
ApplyTransformation(TransformationInlineFunction(
|
||||
function_call_instruction->result_id(), result_id_map));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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_INLINE_FUNCTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Looks for OpFunctionCall instructions and randomly decides which ones to
|
||||
// inline. If the instructions of the called function are going to be inlined,
|
||||
// then a mapping, between their result ids and suitable ids, is done.
|
||||
class FuzzerPassInlineFunctions : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInlineFunctions(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInlineFunctions() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_
|
||||
@@ -1,156 +0,0 @@
|
||||
// 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 "fuzzer_pass_interchange_signedness_of_integer_operands.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
|
||||
|
||||
void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
|
||||
// Make vector keeping track of all the uses we want to replace.
|
||||
// This is a vector of pairs, where the first element is an id use descriptor
|
||||
// identifying the use of a constant id and the second is the id that should
|
||||
// be used to replace it.
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
|
||||
|
||||
for (auto constant : GetIRContext()->GetConstants()) {
|
||||
uint32_t constant_id = constant->result_id();
|
||||
|
||||
// We want to record the synonymity of an integer constant with another
|
||||
// constant with opposite signedness, and this can only be done if they are
|
||||
// not irrelevant.
|
||||
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
constant_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t toggled_id =
|
||||
FindOrCreateToggledIntegerConstant(constant->result_id());
|
||||
if (!toggled_id) {
|
||||
// Not an integer constant
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
toggled_id) &&
|
||||
"FindOrCreateToggledConstant can't produce an irrelevant id");
|
||||
|
||||
// Record synonymous constants
|
||||
ApplyTransformation(
|
||||
TransformationRecordSynonymousConstants(constant_id, toggled_id));
|
||||
|
||||
// Find all the uses of the constant and, for each, probabilistically
|
||||
// decide whether to replace it.
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
constant_id,
|
||||
[this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
|
||||
uint32_t use_index) -> void {
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfInterchangingSignednessOfIntegerOperands())) {
|
||||
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
|
||||
&uses_to_replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids if it is allowed.
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second));
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
if (!constant->AsIntConstant() &&
|
||||
(!constant->AsVectorConstant() ||
|
||||
!constant->AsVectorConstant()->component_type()->AsInteger())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auto integer = constant->AsIntConstant()) {
|
||||
auto type = integer->type()->AsInteger();
|
||||
|
||||
// Find or create and return the toggled constant.
|
||||
return FindOrCreateIntegerConstant(std::vector<uint32_t>(integer->words()),
|
||||
type->width(), !type->IsSigned(), false);
|
||||
}
|
||||
|
||||
// The constant is an integer vector.
|
||||
|
||||
// Find the component type.
|
||||
auto component_type =
|
||||
constant->AsVectorConstant()->component_type()->AsInteger();
|
||||
|
||||
// Find or create the toggled component type.
|
||||
uint32_t toggled_component_type = FindOrCreateIntegerType(
|
||||
component_type->width(), !component_type->IsSigned());
|
||||
|
||||
// Get the information about the toggled components. We need to extract this
|
||||
// information now because the analyses might be invalidated, which would make
|
||||
// the constant and component_type variables invalid.
|
||||
std::vector<std::vector<uint32_t>> component_words;
|
||||
|
||||
for (auto component : constant->AsVectorConstant()->GetComponents()) {
|
||||
component_words.push_back(component->AsIntConstant()->words());
|
||||
}
|
||||
uint32_t width = component_type->width();
|
||||
bool is_signed = !component_type->IsSigned();
|
||||
|
||||
std::vector<uint32_t> toggled_components;
|
||||
|
||||
// Find or create the toggled components.
|
||||
for (auto words : component_words) {
|
||||
toggled_components.push_back(
|
||||
FindOrCreateIntegerConstant(words, width, is_signed, false));
|
||||
}
|
||||
|
||||
// Find or create the required toggled vector type.
|
||||
uint32_t toggled_type = FindOrCreateVectorType(
|
||||
toggled_component_type, (uint32_t)toggled_components.size());
|
||||
|
||||
// Find or create and return the toggled vector constant.
|
||||
return FindOrCreateCompositeConstant(toggled_components, toggled_type, false);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,51 +0,0 @@
|
||||
// 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_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that:
|
||||
// - Finds all the integer constant (scalar and vector) definitions in the
|
||||
// module and adds the definitions of the integer with the same data words but
|
||||
// opposite signedness. If the synonym is already in the module, it does not
|
||||
// add a new one.
|
||||
// - For each use of an integer constant where its signedness does not matter,
|
||||
// decides whether to change it to the id of the toggled constant.
|
||||
class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Given the id of an integer constant (scalar or vector), it finds or creates
|
||||
// the corresponding toggled constant (the integer with the same data words
|
||||
// but opposite signedness). Returns the id of the toggled instruction if the
|
||||
// constant is an integer scalar or vector, 0 otherwise.
|
||||
uint32_t FindOrCreateToggledIntegerConstant(uint32_t id);
|
||||
};
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright (c) 2020 Stefano Milizia
|
||||
// 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_interchange_zero_like_constants.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInterchangeZeroLikeConstants::
|
||||
~FuzzerPassInterchangeZeroLikeConstants() = default;
|
||||
|
||||
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());
|
||||
|
||||
// This pass only toggles zero-like constants
|
||||
if (!constant->IsZero()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (constant->AsScalarConstant()) {
|
||||
return FindOrCreateNullConstant(declaration->type_id());
|
||||
} else if (constant->AsNullConstant()) {
|
||||
// Add declaration of equivalent scalar constant
|
||||
auto kind = constant->type()->kind();
|
||||
if (kind == opt::analysis::Type::kBool ||
|
||||
kind == opt::analysis::Type::kInteger ||
|
||||
kind == opt::analysis::Type::kFloat) {
|
||||
return FindOrCreateZeroConstant(declaration->type_id(), false);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FuzzerPassInterchangeZeroLikeConstants::Apply() {
|
||||
// Make vector keeping track of all the uses we want to replace.
|
||||
// This is a vector of pairs, where the first element is an id use descriptor
|
||||
// identifying the use of a constant id and the second is the id that should
|
||||
// be used to replace it.
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
|
||||
|
||||
for (auto constant : GetIRContext()->GetConstants()) {
|
||||
uint32_t constant_id = constant->result_id();
|
||||
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
constant_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
|
||||
if (!toggled_id) {
|
||||
// Not a zero-like constant
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
toggled_id) &&
|
||||
"FindOrCreateToggledConstant can't produce an irrelevant id");
|
||||
|
||||
// Record synonymous constants
|
||||
ApplyTransformation(
|
||||
TransformationRecordSynonymousConstants(constant_id, toggled_id));
|
||||
|
||||
// Find all the uses of the constant and, for each, probabilistically
|
||||
// decide whether to replace it.
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
constant_id,
|
||||
[this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
|
||||
uint32_t use_index) -> void {
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfInterchangingZeroLikeConstants())) {
|
||||
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
|
||||
&uses_to_replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids if it is allowed.
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second));
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,53 +0,0 @@
|
||||
// Copyright (c) 2020 Stefano Milizia
|
||||
// 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_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that:
|
||||
// - Finds all the zero-like constant definitions in the module and adds the
|
||||
// definitions of the corresponding synonym, recording the fact that they
|
||||
// are synonymous. If the synonym is already in the module, it does not
|
||||
// add a new one.
|
||||
// - For each use of a zero-like constant, decides whether to change it to the
|
||||
// id of the toggled constant.
|
||||
class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInterchangeZeroLikeConstants() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Given the declaration of a zero-like constant, it finds or creates the
|
||||
// corresponding toggled constant (a scalar constant of value 0 becomes a
|
||||
// null constant of the same type and vice versa).
|
||||
// Returns the id of the toggled instruction if the constant is zero-like,
|
||||
// 0 otherwise.
|
||||
uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_invert_comparison_operator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() =
|
||||
default;
|
||||
|
||||
void FuzzerPassInvertComparisonOperators::Apply() {
|
||||
GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) {
|
||||
if (!TransformationInvertComparisonOperator::IsInversionSupported(
|
||||
inst->opcode())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationInvertComparisonOperator(
|
||||
inst->result_id(), GetFuzzerContext()->GetFreshId()));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Iterates over different comparison operators in the module (>=, <, > etc.)
|
||||
// and randomly decides whether to invert each one or not.
|
||||
class FuzzerPassInvertComparisonOperators : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInvertComparisonOperators(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInvertComparisonOperators() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
|
||||
@@ -1,71 +0,0 @@
|
||||
// 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
|
||||
@@ -1,40 +0,0 @@
|
||||
// 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_
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2019 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_merge_blocks.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/transformation_merge_blocks.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassMergeBlocks::FuzzerPassMergeBlocks(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default;
|
||||
|
||||
void FuzzerPassMergeBlocks::Apply() {
|
||||
// First we populate a sequence of transformations that we might consider
|
||||
// applying.
|
||||
std::vector<TransformationMergeBlocks> potential_transformations;
|
||||
// We do this by considering every block of every function.
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// We probabilistically decide to ignore some blocks.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfMergingBlocks())) {
|
||||
continue;
|
||||
}
|
||||
// For other blocks, we add a transformation to merge the block into its
|
||||
// predecessor if that transformation would be applicable.
|
||||
TransformationMergeBlocks transformation(block.id());
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
potential_transformations.push_back(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!potential_transformations.empty()) {
|
||||
auto transformation =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations);
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user