Updated spirv-tools.
This commit is contained in:
parent
381d381c93
commit
fd71b4060d
38
3rdparty/spirv-tools/CHANGES
vendored
38
3rdparty/spirv-tools/CHANGES
vendored
@ -1,7 +1,41 @@
|
||||
Revision history for SPIRV-Tools
|
||||
|
||||
v2020.4-dev 2020-05-27
|
||||
- Start v2020.4-dev
|
||||
v2020.5 2020-07-22
|
||||
- Start SPIRV-Tools v2020.5
|
||||
|
||||
v2020.4 2020-07-22
|
||||
- General
|
||||
- Changed variable names to be more descriptive (#3433)
|
||||
- Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512)
|
||||
- Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463)
|
||||
- Support SPV_KHR_terminate_invocation (#3568)
|
||||
- Optimizer
|
||||
- Preserving debug information in optimizations
|
||||
(#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542)
|
||||
- Eliminate branches with condition of OpConstantNull (#3438)
|
||||
- Use structured order to unroll loops. (#3443)
|
||||
- Updated desc_sroa to support flattening structures (#3448)
|
||||
- Support OpCompositeExtract pattern in desc_sroa (#3456)
|
||||
- Fix ADCE pass bug for mulitple entries (#3470)
|
||||
- Sink pointer instructions in merge return (#3569)
|
||||
- Validator
|
||||
- Validate location assignments (#3308)
|
||||
- Fix reachability in the validator (#3541)
|
||||
- Reduce
|
||||
- Fuzz
|
||||
- Add support for OpSpecConstant* (#3373)
|
||||
- Add replace linear algebra instruction transformation (#3402)
|
||||
- Implement vector shuffle fuzzer pass (#3412)
|
||||
- Swap operands in OpBranchConditional (#3423)
|
||||
- Permute OpPhi instruction operands (#3421)
|
||||
- Add FuzzerPassAddCopyMemoryInstructions (#3391)
|
||||
- TransformationInvertComparisonOperator (#3475)
|
||||
- Add variables with workgroup storage class (#3485)
|
||||
- Add image sample unused components transformation (#3439)
|
||||
- TransformationReplaceParameterWithGlobal (#3434)
|
||||
- Support adding dead break from back-edge block (#3519)
|
||||
- Fuzzer pass to interchange zero-like constants (#3524)
|
||||
- Linker
|
||||
|
||||
v2020.3 2020-05-27
|
||||
- General
|
||||
|
@ -1 +1 @@
|
||||
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898"
|
||||
"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"
|
||||
|
@ -2,6 +2,7 @@ static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddre
|
||||
static const SpvCapability pygen_variable_caps_AddressesPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityPhysicalStorageBufferAddresses};
|
||||
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer};
|
||||
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer, SpvCapabilityPhysicalStorageBufferAddresses};
|
||||
static const SpvCapability pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT};
|
||||
static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL};
|
||||
static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
|
||||
static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
|
||||
@ -55,6 +56,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_demote_to_helper_invocation[] = {spvtools::Extension::kSPV_EXT_demote_to_helper_invocation};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_decorate_stringSPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_decorate_string, spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_INTEL_blocking_pipes[] = {spvtools::Extension::kSPV_INTEL_blocking_pipes};
|
||||
@ -65,6 +67,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_KHR_ray_query[] = {spvt
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_ballot[] = {spvtools::Extension::kSPV_KHR_shader_ballot};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_clock[] = {spvtools::Extension::kSPV_KHR_shader_clock};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_KHR_subgroup_vote[] = {spvtools::Extension::kSPV_KHR_subgroup_vote};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_KHR_terminate_invocation[] = {spvtools::Extension::kSPV_KHR_terminate_invocation};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NV_mesh_shader};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_NV_ray_tracingSPV_KHR_ray_tracing[] = {spvtools::Extension::kSPV_NV_ray_tracing, spvtools::Extension::kSPV_KHR_ray_tracing};
|
||||
@ -417,6 +420,7 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
|
||||
{"PtrEqual", SpvOpPtrEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
|
||||
{"PtrNotEqual", SpvOpPtrNotEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
|
||||
{"PtrDiff", SpvOpPtrDiff, 3, pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
|
||||
{"TerminateInvocation", SpvOpTerminateInvocation, 1, pygen_variable_caps_Shader, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_KHR_terminate_invocation, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupBallotKHR", SpvOpSubgroupBallotKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupFirstInvocationKHR", SpvOpSubgroupFirstInvocationKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu},
|
||||
{"SubgroupAllKHR", SpvOpSubgroupAllKHR, 1, pygen_variable_caps_SubgroupVoteKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_subgroup_vote, 0xffffffffu, 0xffffffffu},
|
||||
@ -633,5 +637,6 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
|
||||
{"RayQueryGetWorldRayDirectionKHR", SpvOpRayQueryGetWorldRayDirectionKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
|
||||
{"RayQueryGetWorldRayOriginKHR", SpvOpRayQueryGetWorldRayOriginKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
|
||||
{"RayQueryGetIntersectionObjectToWorldKHR", SpvOpRayQueryGetIntersectionObjectToWorldKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
|
||||
{"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}
|
||||
{"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
|
||||
{"AtomicFAddEXT", SpvOpAtomicFAddEXT, 2, pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT, 6, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_SCOPE_ID, SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, 0xffffffffu, 0xffffffffu}
|
||||
};
|
@ -32,6 +32,8 @@ const char* ExtensionToString(Extension extension) {
|
||||
return "SPV_EXT_fragment_shader_interlock";
|
||||
case Extension::kSPV_EXT_physical_storage_buffer:
|
||||
return "SPV_EXT_physical_storage_buffer";
|
||||
case Extension::kSPV_EXT_shader_atomic_float_add:
|
||||
return "SPV_EXT_shader_atomic_float_add";
|
||||
case Extension::kSPV_EXT_shader_stencil_export:
|
||||
return "SPV_EXT_shader_stencil_export";
|
||||
case Extension::kSPV_EXT_shader_viewport_index_layer:
|
||||
@ -98,6 +100,8 @@ const char* ExtensionToString(Extension extension) {
|
||||
return "SPV_KHR_storage_buffer_storage_class";
|
||||
case Extension::kSPV_KHR_subgroup_vote:
|
||||
return "SPV_KHR_subgroup_vote";
|
||||
case Extension::kSPV_KHR_terminate_invocation:
|
||||
return "SPV_KHR_terminate_invocation";
|
||||
case Extension::kSPV_KHR_variable_pointers:
|
||||
return "SPV_KHR_variable_pointers";
|
||||
case Extension::kSPV_KHR_vulkan_memory_model:
|
||||
@ -139,8 +143,8 @@ const char* ExtensionToString(Extension extension) {
|
||||
|
||||
|
||||
bool GetExtensionFromString(const char* str, Extension* extension) {
|
||||
static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_blocking_pipes", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_kernel_attributes", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_non_semantic_info", "SPV_KHR_physical_storage_buffer", "SPV_KHR_post_depth_coverage", "SPV_KHR_ray_query", "SPV_KHR_ray_tracing", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_clock", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" };
|
||||
static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_demote_to_helper_invocation, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_non_semantic_info, Extension::kSPV_KHR_physical_storage_buffer, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_ray_query, Extension::kSPV_KHR_ray_tracing, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_clock, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique };
|
||||
static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_atomic_float_add", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_blocking_pipes", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_fpga_loop_controls", "SPV_INTEL_fpga_memory_attributes", "SPV_INTEL_fpga_reg", "SPV_INTEL_function_pointers", "SPV_INTEL_kernel_attributes", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_INTEL_unstructured_loop_controls", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_non_semantic_info", "SPV_KHR_physical_storage_buffer", "SPV_KHR_post_depth_coverage", "SPV_KHR_ray_query", "SPV_KHR_ray_tracing", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_clock", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_terminate_invocation", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" };
|
||||
static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_demote_to_helper_invocation, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_atomic_float_add, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_blocking_pipes, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_fpga_loop_controls, Extension::kSPV_INTEL_fpga_memory_attributes, Extension::kSPV_INTEL_fpga_reg, Extension::kSPV_INTEL_function_pointers, Extension::kSPV_INTEL_kernel_attributes, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_INTEL_unstructured_loop_controls, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_non_semantic_info, Extension::kSPV_KHR_physical_storage_buffer, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_ray_query, Extension::kSPV_KHR_ray_tracing, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_clock, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_terminate_invocation, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique };
|
||||
const auto b = std::begin(known_ext_strs);
|
||||
const auto e = std::end(known_ext_strs);
|
||||
const auto found = std::equal_range(
|
||||
@ -460,6 +464,10 @@ const char* CapabilityToString(SpvCapability capability) {
|
||||
return "BlockingPipesINTEL";
|
||||
case SpvCapabilityFPGARegINTEL:
|
||||
return "FPGARegINTEL";
|
||||
case SpvCapabilityAtomicFloat32AddEXT:
|
||||
return "AtomicFloat32AddEXT";
|
||||
case SpvCapabilityAtomicFloat64AddEXT:
|
||||
return "AtomicFloat64AddEXT";
|
||||
case SpvCapabilityMax:
|
||||
assert(0 && "Attempting to convert SpvCapabilityMax to string");
|
||||
return "";
|
||||
|
@ -14,6 +14,7 @@ kSPV_EXT_fragment_fully_covered,
|
||||
kSPV_EXT_fragment_invocation_density,
|
||||
kSPV_EXT_fragment_shader_interlock,
|
||||
kSPV_EXT_physical_storage_buffer,
|
||||
kSPV_EXT_shader_atomic_float_add,
|
||||
kSPV_EXT_shader_stencil_export,
|
||||
kSPV_EXT_shader_viewport_index_layer,
|
||||
kSPV_GOOGLE_decorate_string,
|
||||
@ -47,6 +48,7 @@ kSPV_KHR_shader_clock,
|
||||
kSPV_KHR_shader_draw_parameters,
|
||||
kSPV_KHR_storage_buffer_storage_class,
|
||||
kSPV_KHR_subgroup_vote,
|
||||
kSPV_KHR_terminate_invocation,
|
||||
kSPV_KHR_variable_pointers,
|
||||
kSPV_KHR_vulkan_memory_model,
|
||||
kSPV_NVX_multiview_per_view_attributes,
|
||||
|
@ -108,6 +108,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_fully_cove
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_invocation_densitySPV_NV_shading_rate[] = {spvtools::Extension::kSPV_EXT_fragment_invocation_density, spvtools::Extension::kSPV_NV_shading_rate};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer[] = {spvtools::Extension::kSPV_EXT_physical_storage_buffer, spvtools::Extension::kSPV_KHR_physical_storage_buffer};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_stencil_export[] = {spvtools::Extension::kSPV_EXT_shader_stencil_export};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_viewport_index_layerSPV_NV_viewport_array2[] = {spvtools::Extension::kSPV_EXT_shader_viewport_index_layer, spvtools::Extension::kSPV_NV_viewport_array2};
|
||||
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
|
||||
@ -962,7 +963,9 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
|
||||
{"KernelAttributesINTEL", 5892, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"FPGAKernelAttributesINTEL", 5897, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"BlockingPipesINTEL", 5945, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_blocking_pipes, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu}
|
||||
{"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"AtomicFloat32AddEXT", 6033, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu},
|
||||
{"AtomicFloat64AddEXT", 6034, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu}
|
||||
};
|
||||
|
||||
static const spv_operand_desc_t pygen_variable_RayQueryIntersectionEntries[] = {
|
||||
|
@ -876,8 +876,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass();
|
||||
// for the first index.
|
||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
|
||||
|
||||
// Create a pass to replace all OpKill instruction with a function call to a
|
||||
// function that has a single OpKill. This allows more code to be inlined.
|
||||
// Create a pass to replace each OpKill instruction with a function call to a
|
||||
// function that has a single OpKill. Also replace each OpTerminateInvocation
|
||||
// instruction with a function call to a function that has a single
|
||||
// OpTerminateInvocation. This allows more code to be inlined.
|
||||
Optimizer::PassToken CreateWrapOpKillPass();
|
||||
|
||||
// Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and
|
||||
|
16
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
16
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@ -52,6 +52,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_local_variables.h
|
||||
fuzzer_pass_add_no_contraction_decorations.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
|
||||
@ -72,8 +73,11 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_permute_function_parameters.h
|
||||
fuzzer_pass_permute_phi_operands.h
|
||||
fuzzer_pass_push_ids_through_variables.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_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
|
||||
@ -104,6 +108,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_local_variable.h
|
||||
transformation_add_no_contraction_decoration.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
|
||||
@ -133,9 +138,12 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_record_synonymous_constants.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_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
|
||||
@ -171,6 +179,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_local_variables.cpp
|
||||
fuzzer_pass_add_no_contraction_decorations.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
|
||||
@ -191,8 +200,11 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_permute_function_parameters.cpp
|
||||
fuzzer_pass_permute_phi_operands.cpp
|
||||
fuzzer_pass_push_ids_through_variables.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_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
|
||||
@ -222,6 +234,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_local_variable.cpp
|
||||
transformation_add_no_contraction_decoration.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
|
||||
@ -251,9 +264,12 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_record_synonymous_constants.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_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
|
||||
|
@ -1343,32 +1343,49 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Irrelevant pointee value facts
|
||||
// Irrelevant value facts
|
||||
|
||||
// The purpose of this class is to group the fields and data used to represent
|
||||
// facts about pointers whose pointee values are irrelevant.
|
||||
class FactManager::IrrelevantPointeeValueFacts {
|
||||
// facts about various irrelevant values in the module.
|
||||
class FactManager::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::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
|
||||
std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
|
||||
std::unordered_set<uint32_t> irrelevant_ids_;
|
||||
};
|
||||
|
||||
void FactManager::IrrelevantPointeeValueFacts::AddFact(
|
||||
void FactManager::IrrelevantValueFacts::AddFact(
|
||||
const protobufs::FactPointeeValueIsIrrelevant& fact) {
|
||||
pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
|
||||
}
|
||||
|
||||
bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
|
||||
void FactManager::IrrelevantValueFacts::AddFact(
|
||||
const protobufs::FactIdIsIrrelevant& fact) {
|
||||
irrelevant_ids_.insert(fact.result_id());
|
||||
}
|
||||
|
||||
bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant(
|
||||
uint32_t pointer_id) const {
|
||||
return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
|
||||
}
|
||||
|
||||
bool FactManager::IrrelevantValueFacts::IdIsIrrelevant(
|
||||
uint32_t pointer_id) const {
|
||||
return irrelevant_ids_.count(pointer_id) != 0;
|
||||
}
|
||||
|
||||
// End of arbitrarily-valued variable facts
|
||||
//==============================
|
||||
|
||||
@ -1378,8 +1395,7 @@ FactManager::FactManager()
|
||||
MakeUnique<DataSynonymAndIdEquationFacts>()),
|
||||
dead_block_facts_(MakeUnique<DeadBlockFacts>()),
|
||||
livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
|
||||
irrelevant_pointee_value_facts_(
|
||||
MakeUnique<IrrelevantPointeeValueFacts>()) {}
|
||||
irrelevant_value_facts_(MakeUnique<IrrelevantValueFacts>()) {}
|
||||
|
||||
FactManager::~FactManager() = default;
|
||||
|
||||
@ -1420,6 +1436,8 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
|
||||
void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
|
||||
const protobufs::DataDescriptor& data2,
|
||||
opt::IRContext* context) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
|
||||
// assert that neither |data1| nor |data2| are irrelevant.
|
||||
protobufs::FactDataSynonym fact;
|
||||
*fact.mutable_data1() = data1;
|
||||
*fact.mutable_data2() = data2;
|
||||
@ -1500,18 +1518,32 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
|
||||
}
|
||||
|
||||
bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
|
||||
return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
|
||||
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_pointee_value_facts_->AddFact(fact);
|
||||
irrelevant_value_facts_->AddFact(fact);
|
||||
}
|
||||
|
||||
void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
|
||||
// assert that |result_id| is not a part of any DataSynonym fact.
|
||||
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) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
|
||||
// assert that elements of |rhs_id| and |lhs_id| are not irrelevant.
|
||||
protobufs::FactIdEquation fact;
|
||||
fact.set_lhs_id(lhs_id);
|
||||
fact.set_opcode(opcode);
|
||||
|
19
3rdparty/spirv-tools/source/fuzz/fact_manager.h
vendored
19
3rdparty/spirv-tools/source/fuzz/fact_manager.h
vendored
@ -68,6 +68,10 @@ class FactManager {
|
||||
// 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]|
|
||||
@ -181,13 +185,16 @@ class FactManager {
|
||||
//==============================
|
||||
|
||||
//==============================
|
||||
// Querying facts about pointers with irrelevant pointee values
|
||||
// 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;
|
||||
|
||||
// End of irrelevant pointee value facts
|
||||
// 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:
|
||||
@ -213,10 +220,10 @@ class FactManager {
|
||||
std::unique_ptr<LivesafeFunctionFacts>
|
||||
livesafe_function_facts_; // Unique pointer to internal data.
|
||||
|
||||
class IrrelevantPointeeValueFacts; // Opaque class for management of
|
||||
// facts about pointers whose pointee values do not matter.
|
||||
std::unique_ptr<IrrelevantPointeeValueFacts>
|
||||
irrelevant_pointee_value_facts_; // Unique pointer to internal data.
|
||||
class IrrelevantValueFacts; // Opaque class for management of
|
||||
// facts about various irrelevant values in the module.
|
||||
std::unique_ptr<IrrelevantValueFacts>
|
||||
irrelevant_value_facts_; // Unique pointer to internal data.
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
4
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
4
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@ -57,6 +57,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
|
||||
#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"
|
||||
@ -288,6 +289,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
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);
|
||||
|
@ -42,6 +42,7 @@ 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> 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};
|
||||
@ -76,11 +77,17 @@ const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
|
||||
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> kChanceOfPushingIdThroughVariable = {5, 50};
|
||||
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> 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};
|
||||
@ -98,6 +105,7 @@ const uint32_t kDefaultMaxNewArraySizeLimit = 100;
|
||||
// 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.
|
||||
@ -123,6 +131,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
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_ =
|
||||
@ -154,6 +164,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
|
||||
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);
|
||||
@ -202,12 +214,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
|
||||
chance_of_pushing_id_through_variable_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
|
||||
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_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);
|
||||
|
@ -143,6 +143,9 @@ class FuzzerContext {
|
||||
return chance_of_adding_no_contraction_decoration_;
|
||||
}
|
||||
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() {
|
||||
@ -209,6 +212,12 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfPushingIdThroughVariable() {
|
||||
return chance_of_pushing_id_through_variable_;
|
||||
}
|
||||
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_;
|
||||
}
|
||||
@ -218,6 +227,9 @@ class FuzzerContext {
|
||||
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_;
|
||||
@ -234,6 +246,9 @@ class FuzzerContext {
|
||||
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};
|
||||
|
||||
@ -275,6 +290,12 @@ class FuzzerContext {
|
||||
{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;
|
||||
@ -312,6 +333,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_adding_matrix_type_;
|
||||
uint32_t chance_of_adding_no_contraction_decoration_;
|
||||
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_;
|
||||
@ -338,9 +360,12 @@ class FuzzerContext {
|
||||
uint32_t chance_of_permuting_parameters_;
|
||||
uint32_t chance_of_permuting_phi_operands_;
|
||||
uint32_t chance_of_pushing_id_through_variable_;
|
||||
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_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_;
|
||||
@ -355,6 +380,7 @@ class FuzzerContext {
|
||||
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.
|
||||
|
189
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
189
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@ -153,9 +153,7 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateBoolType() {
|
||||
opt::analysis::Bool bool_type;
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
|
||||
if (existing_id) {
|
||||
if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
@ -277,62 +275,52 @@ uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateIntegerConstant(
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed) {
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
|
||||
bool is_irrelevant) {
|
||||
auto int_type_id = FindOrCreateIntegerType(width, is_signed);
|
||||
opt::analysis::IntConstant int_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(int_type_id)->AsInteger(), words);
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
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));
|
||||
ApplyTransformation(TransformationAddConstantScalar(result, int_type_id,
|
||||
words, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateFloatConstant(
|
||||
const std::vector<uint32_t>& words, uint32_t width) {
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
|
||||
auto float_type_id = FindOrCreateFloatType(width);
|
||||
opt::analysis::FloatConstant float_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), words);
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
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));
|
||||
ApplyTransformation(TransformationAddConstantScalar(result, float_type_id,
|
||||
words, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
|
||||
uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) {
|
||||
auto bool_type_id = FindOrCreateBoolType();
|
||||
opt::analysis::BoolConstant bool_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
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));
|
||||
ApplyTransformation(
|
||||
TransformationAddConstantBoolean(result, value, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t type_id) {
|
||||
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);
|
||||
@ -340,12 +328,12 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
|
||||
if (type->AsBool()) {
|
||||
assert(words.size() == 1);
|
||||
return FindOrCreateBoolConstant(words[0]);
|
||||
return FindOrCreateBoolConstant(words[0], is_irrelevant);
|
||||
} else if (const auto* integer = type->AsInteger()) {
|
||||
return FindOrCreateIntegerConstant(words, integer->width(),
|
||||
integer->IsSigned());
|
||||
integer->IsSigned(), is_irrelevant);
|
||||
} else if (const auto* floating = type->AsFloat()) {
|
||||
return FindOrCreateFloatConstant(words, floating->width());
|
||||
return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant);
|
||||
}
|
||||
|
||||
// This assertion will fail in debug build but not in release build
|
||||
@ -355,21 +343,17 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateCompositeConstant(
|
||||
const std::vector<uint32_t>& component_ids, uint32_t type_id) {
|
||||
assert(type_id && "|type_id| can't be 0");
|
||||
const auto* type_inst = GetIRContext()->get_def_use_mgr()->GetDef(type_id);
|
||||
assert(type_inst && "|type_id| is invalid");
|
||||
|
||||
std::vector<const opt::analysis::Constant*> constants;
|
||||
for (auto id : component_ids) {
|
||||
assert(id && "Component's id can't be 0");
|
||||
const auto* constant =
|
||||
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
|
||||
assert(constant && "Component's id is invalid");
|
||||
constants.push_back(constant);
|
||||
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;
|
||||
}
|
||||
|
||||
return FindOrCreateCompositeConstant(*type_inst, constants, component_ids);
|
||||
uint32_t result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantComposite(
|
||||
result, type_id, component_ids, is_irrelevant));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
||||
@ -474,51 +458,55 @@ FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||
uint32_t scalar_or_composite_type_id) {
|
||||
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);
|
||||
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);
|
||||
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));
|
||||
type_instruction->GetSingleWordInOperand(1), is_irrelevant);
|
||||
}
|
||||
case SpvOpTypeArray: {
|
||||
return GetZeroConstantForHomogeneousComposite(
|
||||
*type_instruction, type_instruction->GetSingleWordInOperand(0),
|
||||
fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
|
||||
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: {
|
||||
return GetZeroConstantForHomogeneousComposite(
|
||||
*type_instruction, type_instruction->GetSingleWordInOperand(0),
|
||||
type_instruction->GetSingleWordInOperand(1));
|
||||
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<const opt::analysis::Constant*> field_zero_constants;
|
||||
std::vector<uint32_t> field_zero_ids;
|
||||
for (uint32_t index = 0; index < type_instruction->NumInOperands();
|
||||
index++) {
|
||||
uint32_t field_constant_id = FindOrCreateZeroConstant(
|
||||
type_instruction->GetSingleWordInOperand(index));
|
||||
field_zero_ids.push_back(field_constant_id);
|
||||
field_zero_constants.push_back(
|
||||
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
|
||||
field_constant_id));
|
||||
field_zero_ids.push_back(FindOrCreateZeroConstant(
|
||||
type_instruction->GetSingleWordInOperand(index), is_irrelevant));
|
||||
}
|
||||
return FindOrCreateCompositeConstant(
|
||||
*type_instruction, field_zero_constants, field_zero_ids);
|
||||
field_zero_ids, scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
default:
|
||||
assert(false && "Unknown type.");
|
||||
@ -526,62 +514,5 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateCompositeConstant(
|
||||
const opt::Instruction& composite_type_instruction,
|
||||
const std::vector<const opt::analysis::Constant*>& constants,
|
||||
const std::vector<uint32_t>& constant_ids) {
|
||||
assert(constants.size() == constant_ids.size() &&
|
||||
"Precondition: |constants| and |constant_ids| must be in "
|
||||
"correspondence.");
|
||||
|
||||
opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
|
||||
composite_type_instruction.result_id());
|
||||
std::unique_ptr<opt::analysis::Constant> composite_constant;
|
||||
if (composite_type->AsArray()) {
|
||||
composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
|
||||
composite_type->AsArray(), constants);
|
||||
} else if (composite_type->AsMatrix()) {
|
||||
composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
|
||||
composite_type->AsMatrix(), constants);
|
||||
} else if (composite_type->AsStruct()) {
|
||||
composite_constant = MakeUnique<opt::analysis::StructConstant>(
|
||||
composite_type->AsStruct(), constants);
|
||||
} else if (composite_type->AsVector()) {
|
||||
composite_constant = MakeUnique<opt::analysis::VectorConstant>(
|
||||
composite_type->AsVector(), constants);
|
||||
} else {
|
||||
assert(false &&
|
||||
"Precondition: |composite_type| must declare a composite type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
|
||||
composite_constant.get(), composite_type_instruction.result_id());
|
||||
if (existing_constant) {
|
||||
return existing_constant;
|
||||
}
|
||||
uint32_t result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantComposite(
|
||||
result, composite_type_instruction.result_id(), constant_ids));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
|
||||
const opt::Instruction& composite_type_instruction,
|
||||
uint32_t component_type_id, uint32_t num_components) {
|
||||
std::vector<const opt::analysis::Constant*> zero_constants;
|
||||
std::vector<uint32_t> zero_ids;
|
||||
uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
|
||||
const opt::analysis::Constant* registered_zero_component =
|
||||
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
|
||||
for (uint32_t i = 0; i < num_components; i++) {
|
||||
zero_constants.push_back(registered_zero_component);
|
||||
zero_ids.push_back(zero_component);
|
||||
}
|
||||
return FindOrCreateCompositeConstant(composite_type_instruction,
|
||||
zero_constants, zero_ids);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
60
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
60
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@ -174,32 +174,48 @@ class FuzzerPass {
|
||||
// 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);
|
||||
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.
|
||||
// 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);
|
||||
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.
|
||||
uint32_t FindOrCreateBoolConstant(bool value);
|
||||
// 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.
|
||||
// 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);
|
||||
uint32_t type_id, bool is_irrelevant);
|
||||
|
||||
// Returns the id of an OpConstantComposite
|
||||
// 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);
|
||||
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|
|
||||
@ -232,7 +248,8 @@ class FuzzerPass {
|
||||
// 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.
|
||||
// add it. The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
//
|
||||
// Examples:
|
||||
// --------------+-------------------------------
|
||||
@ -254,31 +271,10 @@ class FuzzerPass {
|
||||
// uint2 u; |
|
||||
// } |
|
||||
// --------------+-------------------------------
|
||||
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
|
||||
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
private:
|
||||
// Array, matrix and vector are *homogeneous* composite types in the sense
|
||||
// that every component of one of these types has the same type. Given a
|
||||
// homogeneous composite type instruction, |composite_type_instruction|,
|
||||
// returns the id of a composite constant instruction for which every element
|
||||
// is zero/false. If such an instruction does not yet exist, transformations
|
||||
// are applied to add it.
|
||||
uint32_t GetZeroConstantForHomogeneousComposite(
|
||||
const opt::Instruction& composite_type_instruction,
|
||||
uint32_t component_type_id, uint32_t num_components);
|
||||
|
||||
// Helper to find an existing composite constant instruction of the given
|
||||
// composite type with the given constant components, or to apply
|
||||
// transformations to create such an instruction if it does not yet exist.
|
||||
// Parameter |composite_type_instruction| must be a composite type
|
||||
// instruction. The parameters |constants| and |constant_ids| must have the
|
||||
// same size, and it must be the case that for each i, |constant_ids[i]| is
|
||||
// the result id of an instruction that defines |constants[i]|.
|
||||
uint32_t FindOrCreateCompositeConstant(
|
||||
const opt::Instruction& composite_type_instruction,
|
||||
const std::vector<const opt::analysis::Constant*>& constants,
|
||||
const std::vector<uint32_t>& constant_ids);
|
||||
|
||||
opt::IRContext* ir_context_;
|
||||
TransformationContext* transformation_context_;
|
||||
FuzzerContext* fuzzer_context_;
|
||||
|
@ -138,7 +138,7 @@ void FuzzerPassAddAccessChains::Apply() {
|
||||
uint32_t index_value =
|
||||
GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, GetFuzzerContext()->ChooseEven()));
|
||||
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
|
||||
switch (subobject_type->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
|
@ -97,7 +97,7 @@ void FuzzerPassAddCompositeTypes::AddNewArrayType() {
|
||||
ApplyTransformation(TransformationAddTypeArray(
|
||||
GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
|
||||
FindOrCreateIntegerConstant(
|
||||
{GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false)));
|
||||
{GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false)));
|
||||
}
|
||||
|
||||
void FuzzerPassAddCompositeTypes::AddNewStructType() {
|
||||
|
@ -74,7 +74,7 @@ void FuzzerPassAddCopyMemory::Apply() {
|
||||
ApplyTransformation(TransformationAddCopyMemory(
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId(),
|
||||
inst->result_id(), storage_class,
|
||||
FindOrCreateZeroConstant(pointee_type_id)));
|
||||
FindOrCreateZeroConstant(pointee_type_id, false)));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ void FuzzerPassAddDeadBlocks::Apply() {
|
||||
// Make sure the module contains a boolean constant equal to
|
||||
// |condition_value|.
|
||||
bool condition_value = GetFuzzerContext()->ChooseEven();
|
||||
FindOrCreateBoolConstant(condition_value);
|
||||
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
|
||||
|
@ -68,19 +68,16 @@ void FuzzerPassAddDeadBreaks::Apply() {
|
||||
merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
|
||||
// Add an additional operand for OpPhi instruction.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// If we have a way to communicate to the fact manager
|
||||
// that a specific id use is irrelevant and could be replaced with
|
||||
// something else, we should add such a fact about the zero
|
||||
// provided as an OpPhi operand
|
||||
phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
|
||||
// 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);
|
||||
FindOrCreateBoolConstant(break_condition, false);
|
||||
|
||||
auto candidate_transformation = TransformationAddDeadBreak(
|
||||
block.id(), merge_block->id(), break_condition, std::move(phi_ids));
|
||||
|
@ -59,19 +59,16 @@ void FuzzerPassAddDeadContinues::Apply() {
|
||||
continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
|
||||
// Add an additional operand for OpPhi instruction.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// If we have a way to communicate to the fact manager
|
||||
// that a specific id use is irrelevant and could be replaced with
|
||||
// something else, we should add such a fact about the zero
|
||||
// provided as an OpPhi operand
|
||||
phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
|
||||
// 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);
|
||||
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
|
||||
|
@ -57,9 +57,12 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
std::vector<opt::Instruction*> available_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[](opt::IRContext*, opt::Instruction* instruction) -> bool {
|
||||
[this](opt::IRContext*, opt::Instruction* instruction) -> bool {
|
||||
return instruction->result_id() && instruction->type_id() &&
|
||||
instruction->opcode() != SpvOpUndef;
|
||||
instruction->opcode() != SpvOpUndef &&
|
||||
!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->IdIsIrrelevant(instruction->result_id());
|
||||
});
|
||||
|
||||
// Try the opcodes for which we know how to make ids at random until
|
||||
|
@ -106,148 +106,92 @@ void FuzzerPassAddFunctionCalls::Apply() {
|
||||
});
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::vector<opt::Instruction*>>
|
||||
FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
|
||||
opt::Function* function, opt::BasicBlock* block,
|
||||
const opt::BasicBlock::iterator& inst_it) {
|
||||
// Find all instructions in scope that could potentially be used as actual
|
||||
// parameters. Weed out unsuitable pointer arguments immediately.
|
||||
std::vector<opt::Instruction*> potentially_suitable_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this, block](opt::IRContext* context,
|
||||
opt::Instruction* inst) -> bool {
|
||||
if (!inst->HasResultId() || !inst->type_id()) {
|
||||
// An instruction needs a result id and type in order
|
||||
// to be suitable as an actual parameter.
|
||||
return false;
|
||||
}
|
||||
if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
|
||||
SpvOpTypePointer) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpFunctionParameter:
|
||||
case SpvOpVariable:
|
||||
// Function parameters and variables are the only
|
||||
// kinds of pointer that can be used as actual
|
||||
// parameters.
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
|
||||
block->id()) &&
|
||||
!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->PointeeValueIsIrrelevant(inst->result_id())) {
|
||||
// We can only pass a pointer as an actual parameter
|
||||
// if the pointee value for the pointer is irrelevant,
|
||||
// or if the block from which we would make the
|
||||
// function call is dead.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Group all the instructions that are potentially viable as function actual
|
||||
// parameters by their result types.
|
||||
std::map<uint32_t, std::vector<opt::Instruction*>> result;
|
||||
for (auto inst : potentially_suitable_instructions) {
|
||||
if (result.count(inst->type_id()) == 0) {
|
||||
// This is the first instruction of this type we have seen, so populate
|
||||
// the map with an entry.
|
||||
result.insert({inst->type_id(), {}});
|
||||
}
|
||||
// Add the instruction to the sequence of instructions already associated
|
||||
// with this type.
|
||||
result.at(inst->type_id()).push_back(inst);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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 type_to_available_instructions =
|
||||
GetAvailableInstructionsSuitableForActualParameters(
|
||||
caller_function, caller_block, caller_inst_it);
|
||||
|
||||
opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
callee.DefInst().GetSingleWordInOperand(1));
|
||||
assert(function_type->opcode() == SpvOpTypeFunction &&
|
||||
"The function type does not have the expected opcode.");
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands();
|
||||
arg_index++) {
|
||||
auto arg_type_id =
|
||||
GetIRContext()
|
||||
->get_def_use_mgr()
|
||||
->GetDef(function_type->GetSingleWordInOperand(arg_index))
|
||||
->result_id();
|
||||
if (type_to_available_instructions.count(arg_type_id)) {
|
||||
std::vector<opt::Instruction*>& candidate_arguments =
|
||||
type_to_available_instructions.at(arg_type_id);
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value
|
||||
// selected here is arbitrary. We should consider adding this
|
||||
// information as a fact so that the passed parameter could be
|
||||
// transformed/changed.
|
||||
result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex(
|
||||
candidate_arguments)]
|
||||
->result_id());
|
||||
} else {
|
||||
// We don't have a suitable id in scope to pass, so we must make
|
||||
// something up.
|
||||
auto type_instruction =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id);
|
||||
|
||||
if (type_instruction->opcode() == SpvOpTypePointer) {
|
||||
// In the case of a pointer, we 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);
|
||||
|
||||
// Now bring the variable into existence.
|
||||
auto storage_class = static_cast<SpvStorageClass>(
|
||||
type_instruction->GetSingleWordInOperand(0));
|
||||
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, arg_type_id, caller_function->result_id(),
|
||||
FindOrCreateZeroConstant(
|
||||
type_instruction->GetSingleWordInOperand(1)),
|
||||
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, arg_type_id, storage_class,
|
||||
storage_class == SpvStorageClassPrivate
|
||||
? FindOrCreateZeroConstant(
|
||||
type_instruction->GetSingleWordInOperand(1))
|
||||
: 0,
|
||||
true));
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use
|
||||
// constant zero for the parameter, but could consider adding a fact
|
||||
// to allow further passes to obfuscate it.
|
||||
result.push_back(FindOrCreateZeroConstant(arg_type_id));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -34,14 +34,6 @@ class FuzzerPassAddFunctionCalls : public FuzzerPass {
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Identify all instructions available at |instr_it|, in block |block| of
|
||||
// |function|, that are potentially suitable as function call actual
|
||||
// parameters. The results are grouped by type.
|
||||
std::map<uint32_t, std::vector<opt::Instruction*>>
|
||||
GetAvailableInstructionsSuitableForActualParameters(
|
||||
opt::Function* function, opt::BasicBlock* block,
|
||||
const opt::BasicBlock::iterator& inst_it);
|
||||
|
||||
// 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
|
||||
|
@ -85,7 +85,7 @@ void FuzzerPassAddGlobalVariables::Apply() {
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type_id,
|
||||
variable_storage_class,
|
||||
variable_storage_class == SpvStorageClassPrivate
|
||||
? FindOrCreateZeroConstant(basic_type)
|
||||
? FindOrCreateZeroConstant(basic_type, false)
|
||||
: 0,
|
||||
true));
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ void FuzzerPassAddImageSampleUnusedComponents::Apply() {
|
||||
// FindOrCreateZeroConstant
|
||||
// %20 = OpConstant %4 0
|
||||
// %21 = OpConstantComposite %5 %20 %20
|
||||
FindOrCreateZeroConstant(zero_constant_type_id)},
|
||||
FindOrCreateZeroConstant(zero_constant_type_id, false)},
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction),
|
||||
coordinate_with_unused_components_id));
|
||||
|
||||
|
@ -71,7 +71,7 @@ void FuzzerPassAddLocalVariables::Apply() {
|
||||
}
|
||||
ApplyTransformation(TransformationAddLocalVariable(
|
||||
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
|
||||
FindOrCreateZeroConstant(basic_type), true));
|
||||
FindOrCreateZeroConstant(basic_type, false), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,11 @@ void FuzzerPassAddParameters::Apply() {
|
||||
for (uint32_t i = 0; i < num_new_parameters; ++i) {
|
||||
ApplyTransformation(TransformationAddParameter(
|
||||
function.result_id(), GetFuzzerContext()->GetFreshId(),
|
||||
// We mark the constant as irrelevant so that we can replace it with a
|
||||
// more interesting value later.
|
||||
FindOrCreateZeroConstant(
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(
|
||||
type_candidates)]),
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)],
|
||||
true),
|
||||
GetFuzzerContext()->GetFreshId()));
|
||||
}
|
||||
}
|
||||
|
55
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
vendored
Normal file
55
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// 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
|
39
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
vendored
Normal file
39
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// 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
|
@ -53,11 +53,12 @@ void FuzzerPassAddSynonyms::Apply() {
|
||||
// Select all instructions that can be used to create a synonym to.
|
||||
auto available_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
[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, inst, synonym_type);
|
||||
ir_context, *GetTransformationContext(), inst, synonym_type);
|
||||
});
|
||||
|
||||
if (available_instructions.empty()) {
|
||||
@ -76,7 +77,7 @@ void FuzzerPassAddSynonyms::Apply() {
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
// Create a zero constant to be used as an operand of the synonymous
|
||||
// instruction.
|
||||
FindOrCreateZeroConstant(existing_synonym->type_id());
|
||||
FindOrCreateZeroConstant(existing_synonym->type_id(), false);
|
||||
break;
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
@ -96,13 +97,13 @@ void FuzzerPassAddSynonyms::Apply() {
|
||||
FindOrCreateCompositeConstant(
|
||||
std::vector<uint32_t>(
|
||||
vector->element_count(),
|
||||
FindOrCreateConstant({one_word}, element_type_id)),
|
||||
existing_synonym->type_id());
|
||||
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());
|
||||
existing_synonym->type_id(), false);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
|
@ -60,10 +60,10 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
|
||||
std::vector<opt::Instruction*> vector_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, instruction_iterator,
|
||||
[instruction_descriptor](
|
||||
[this, instruction_descriptor](
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->type_id()) {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -73,6 +73,18 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
|
||||
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),
|
||||
|
@ -142,6 +142,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
: 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),
|
||||
|
@ -14,12 +14,10 @@
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_construct_composites.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_composite_construct.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -67,8 +65,23 @@ void FuzzerPassConstructComposites::Apply() {
|
||||
// program point) and suitable for making a synonym of, associate it
|
||||
// with the id of its result type.
|
||||
TypeIdToInstructions type_id_to_available_instructions;
|
||||
for (auto instruction : FindAvailableInstructions(
|
||||
function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
|
||||
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);
|
||||
}
|
||||
|
@ -55,8 +55,12 @@ void FuzzerPassCopyObjects::Apply() {
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> relevant_instructions =
|
||||
FindAvailableInstructions(function, block, inst_it,
|
||||
fuzzerutil::CanMakeSynonymOf);
|
||||
FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
return fuzzerutil::CanMakeSynonymOf(
|
||||
ir_context, *GetTransformationContext(), inst);
|
||||
});
|
||||
|
||||
// At this point, |relevant_instructions| contains all the instructions
|
||||
// we might think of copying.
|
||||
|
@ -328,7 +328,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
|
||||
ApplyTransformation(TransformationAddTypeArray(
|
||||
new_result_id, original_id_to_donated_id->at(component_type_id),
|
||||
FindOrCreateIntegerConstant(
|
||||
{GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false)));
|
||||
{GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false,
|
||||
false)));
|
||||
} break;
|
||||
case SpvOpTypeStruct: {
|
||||
// Similar to SpvOpTypeArray.
|
||||
@ -446,7 +447,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
|
||||
auto value = type_or_value.opcode() == SpvOpConstantTrue ||
|
||||
type_or_value.opcode() == SpvOpSpecConstantTrue;
|
||||
ApplyTransformation(
|
||||
TransformationAddConstantBoolean(new_result_id, value));
|
||||
TransformationAddConstantBoolean(new_result_id, value, false));
|
||||
} break;
|
||||
case SpvOpSpecConstant:
|
||||
case SpvOpConstant: {
|
||||
@ -459,7 +460,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
|
||||
});
|
||||
ApplyTransformation(TransformationAddConstantScalar(
|
||||
new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
|
||||
data_words));
|
||||
data_words, false));
|
||||
} break;
|
||||
case SpvOpSpecConstantComposite:
|
||||
case SpvOpConstantComposite: {
|
||||
@ -482,7 +483,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
|
||||
});
|
||||
ApplyTransformation(TransformationAddConstantComposite(
|
||||
new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
|
||||
constituent_ids));
|
||||
constituent_ids, false));
|
||||
} break;
|
||||
case SpvOpConstantNull: {
|
||||
if (!original_id_to_donated_id->count(type_or_value.type_id())) {
|
||||
@ -544,7 +545,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
|
||||
? 0
|
||||
: FindOrCreateZeroConstant(
|
||||
fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(), remapped_pointer_type));
|
||||
GetIRContext(), remapped_pointer_type),
|
||||
false);
|
||||
} else {
|
||||
// The variable already had an initializer; use its remapped id.
|
||||
initializer_id = original_id_to_donated_id->at(
|
||||
@ -919,11 +921,10 @@ void FuzzerPassDonateModules::HandleDifficultInstruction(
|
||||
|
||||
// We find or add a zero constant to the receiving module for the type in
|
||||
// question, and add an OpCopyObject instruction that copies this zero.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// Using this particular constant is arbitrary, so if we have a
|
||||
// mechanism for noting that an id use is arbitrary and could be
|
||||
// fuzzed we should use it here.
|
||||
auto zero_constant = FindOrCreateZeroConstant(remapped_type_id);
|
||||
//
|
||||
// We mark the constant as irrelevant so that we can replace it with a
|
||||
// more interesting value later.
|
||||
auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true);
|
||||
donated_instructions->push_back(MakeInstructionMessage(
|
||||
SpvOpCopyObject, remapped_type_id,
|
||||
original_id_to_donated_id->at(instruction.result_id()),
|
||||
@ -980,9 +981,11 @@ void FuzzerPassDonateModules::PrepareInstructionForDonation(
|
||||
// This is an uninitialized local variable. Initialize it to zero.
|
||||
input_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(),
|
||||
original_id_to_donated_id->at(instruction.type_id())))}});
|
||||
{FindOrCreateZeroConstant(
|
||||
fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
GetIRContext(),
|
||||
original_id_to_donated_id->at(instruction.type_id())),
|
||||
false)}});
|
||||
}
|
||||
|
||||
if (instruction.result_id() &&
|
||||
@ -1013,9 +1016,9 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
FindOrCreateBoolType(); // Needed for comparisons
|
||||
FindOrCreatePointerToIntegerType(
|
||||
32, false, SpvStorageClassFunction); // Needed for adding loop limiters
|
||||
FindOrCreateIntegerConstant({0}, 32,
|
||||
FindOrCreateIntegerConstant({0}, 32, false,
|
||||
false); // Needed for initializing loop limiters
|
||||
FindOrCreateIntegerConstant({1}, 32,
|
||||
FindOrCreateIntegerConstant({1}, 32, false,
|
||||
false); // Needed for incrementing loop limiters
|
||||
|
||||
// Get a fresh id for the variable that will be used as a loop limiter.
|
||||
@ -1023,7 +1026,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
// Choose a random loop limit, and add the required constant to the
|
||||
// module if not already there.
|
||||
const uint32_t loop_limit = FindOrCreateIntegerConstant(
|
||||
{GetFuzzerContext()->GetRandomLoopLimit()}, 32, false);
|
||||
{GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false);
|
||||
|
||||
// Consider every loop header in the function to donate, and create a
|
||||
// structure capturing the ids to be used for manipulating the loop
|
||||
@ -1118,7 +1121,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
// whose value is one less than the bound, to compare
|
||||
// against and to use as the clamped value.
|
||||
FindOrCreateIntegerConstant({bound - 1}, 32,
|
||||
index_int_type->IsSigned());
|
||||
index_int_type->IsSigned(), false);
|
||||
}
|
||||
should_be_composite_type =
|
||||
TransformationAddFunction::FollowCompositeIndex(
|
||||
@ -1147,7 +1150,8 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
|
||||
"Function return type must not be a pointer.");
|
||||
kill_unreachable_return_value_id = FindOrCreateZeroConstant(
|
||||
original_id_to_donated_id.at(function_return_type_inst->result_id()));
|
||||
original_id_to_donated_id.at(function_return_type_inst->result_id()),
|
||||
false);
|
||||
}
|
||||
// Add the function in a livesafe manner.
|
||||
ApplyTransformation(TransformationAddFunction(
|
||||
|
@ -14,6 +14,7 @@
|
||||
// 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"
|
||||
@ -49,7 +50,7 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
|
||||
if (kind == opt::analysis::Type::kBool ||
|
||||
kind == opt::analysis::Type::kInteger ||
|
||||
kind == opt::analysis::Type::kFloat) {
|
||||
return FindOrCreateZeroConstant(declaration->type_id());
|
||||
return FindOrCreateZeroConstant(declaration->type_id(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,13 +84,21 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
|
||||
|
||||
for (auto constant : GetIRContext()->GetConstants()) {
|
||||
uint32_t constant_id = constant->result_id();
|
||||
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
|
||||
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));
|
||||
|
@ -53,9 +53,8 @@ void FuzzerPassMergeBlocks::Apply() {
|
||||
}
|
||||
|
||||
while (!potential_transformations.empty()) {
|
||||
uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
|
||||
auto transformation = potential_transformations.at(index);
|
||||
potential_transformations.erase(potential_transformations.begin() + index);
|
||||
auto transformation =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations);
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
@ -311,9 +311,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
|
||||
} while (constant_index_1 == constant_index_2);
|
||||
|
||||
auto constant_id_1 = FindOrCreateConstant(
|
||||
available_constant_words[constant_index_1], chosen_type_id);
|
||||
available_constant_words[constant_index_1], chosen_type_id, false);
|
||||
auto constant_id_2 = FindOrCreateConstant(
|
||||
available_constant_words[constant_index_2], chosen_type_id);
|
||||
available_constant_words[constant_index_2], chosen_type_id, false);
|
||||
|
||||
assert(constant_id_1 != 0 && constant_id_2 != 0 &&
|
||||
"We should not find an available constant with an id of 0.");
|
||||
@ -361,7 +361,7 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
|
||||
// Make sure the module has OpConstant instructions for each index used to
|
||||
// access a uniform.
|
||||
for (auto index : uniform_descriptor.index()) {
|
||||
FindOrCreateIntegerConstant({index}, 32, true);
|
||||
FindOrCreateIntegerConstant({index}, 32, true, false);
|
||||
}
|
||||
|
||||
// Make sure the module has OpTypePointer that points to the element type of
|
||||
|
@ -80,7 +80,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
|
||||
std::vector<opt::Instruction*> value_instructions =
|
||||
FindAvailableInstructions(
|
||||
function, block, instruction_iterator,
|
||||
[basic_type_id, instruction_descriptor](
|
||||
[this, basic_type_id, instruction_descriptor](
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) -> bool {
|
||||
if (!instruction->result_id() || !instruction->type_id()) {
|
||||
@ -91,6 +91,18 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
|
||||
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.
|
||||
if (!GetTransformationContext()
|
||||
->GetFactManager()
|
||||
->IdIsIrrelevant(instruction->result_id()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context,
|
||||
*GetTransformationContext(),
|
||||
instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
ir_context,
|
||||
FindInstruction(instruction_descriptor, ir_context),
|
||||
@ -126,7 +138,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
|
||||
// Create a constant to initialize the variable from. This might update
|
||||
// module's id bound so it must be done before any fresh ids are
|
||||
// computed.
|
||||
auto initializer_id = FindOrCreateZeroConstant(basic_type_id);
|
||||
auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false);
|
||||
|
||||
// Applies the push id through variable transformation.
|
||||
ApplyTransformation(TransformationPushIdThroughVariable(
|
||||
|
58
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
vendored
Normal file
58
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassReplaceCopyMemoriesWithLoadsStores::
|
||||
FuzzerPassReplaceCopyMemoriesWithLoadsStores(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassReplaceCopyMemoriesWithLoadsStores::
|
||||
~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
|
||||
|
||||
void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
// Randomly decide whether to replace the OpCopyMemory.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfReplacingCopyMemoryWithLoadStore())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The instruction must be OpCopyMemory.
|
||||
if (instruction->opcode() != SpvOpCopyMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the transformation replacing OpCopyMemory with OpLoad and OpStore.
|
||||
ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore(
|
||||
GetFuzzerContext()->GetFreshId(),
|
||||
MakeInstructionDescriptor(GetIRContext(), instruction)));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
vendored
Normal file
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Replaces instructions OpCopyMemory with loading the source variable to
|
||||
// an intermediate value and storing this value into the target variable of
|
||||
// the original OpCopyMemory instruction.
|
||||
class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassReplaceCopyMemoriesWithLoadsStores(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
|
83
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
vendored
Normal file
83
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassReplaceCopyObjectsWithStoresLoads::
|
||||
FuzzerPassReplaceCopyObjectsWithStoresLoads(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassReplaceCopyObjectsWithStoresLoads::
|
||||
~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default;
|
||||
|
||||
void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
// Randomly decide whether to replace OpCopyObject.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfReplacingCopyObjectWithStoreLoad())) {
|
||||
return;
|
||||
}
|
||||
// The instruction must be OpCopyObject.
|
||||
if (instruction->opcode() != SpvOpCopyObject) {
|
||||
return;
|
||||
}
|
||||
// The opcode of the type_id instruction cannot be a OpTypePointer,
|
||||
// because we cannot define a pointer to pointer.
|
||||
if (GetIRContext()
|
||||
->get_def_use_mgr()
|
||||
->GetDef(instruction->type_id())
|
||||
->opcode() == SpvOpTypePointer) {
|
||||
return;
|
||||
}
|
||||
// It must be valid to insert OpStore and OpLoad instructions
|
||||
// before the instruction OpCopyObject.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
|
||||
instruction) ||
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Randomly decides whether a global or local variable will be added.
|
||||
auto variable_storage_class = GetFuzzerContext()->ChooseEven()
|
||||
? SpvStorageClassPrivate
|
||||
: SpvStorageClassFunction;
|
||||
|
||||
// Find or create a constant to initialize the variable from.
|
||||
auto variable_initializer_id =
|
||||
FindOrCreateZeroConstant(instruction->type_id(), false);
|
||||
|
||||
// Make sure that pointer type is defined.
|
||||
FindOrCreatePointerType(instruction->type_id(), variable_storage_class);
|
||||
// Apply the transformation replacing OpCopyObject with Store and Load.
|
||||
ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad(
|
||||
instruction->result_id(), GetFuzzerContext()->GetFreshId(),
|
||||
variable_storage_class, variable_initializer_id));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
vendored
Normal file
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Replaces instructions OpCopyObject with storing into a new variable
|
||||
// and immediately loading this variable to |result_id| of the
|
||||
// original OpCopyObject instruction.
|
||||
class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassReplaceCopyObjectsWithStoresLoads(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassReplaceCopyObjectsWithStoresLoads() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
|
@ -81,7 +81,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
|
||||
FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
|
||||
|
||||
// Make sure initializer for the global variable exists in the module.
|
||||
FindOrCreateZeroConstant(replaced_param->type_id());
|
||||
FindOrCreateZeroConstant(replaced_param->type_id(), false);
|
||||
|
||||
ApplyTransformation(TransformationReplaceParameterWithGlobal(
|
||||
GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),
|
||||
|
115
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
vendored
Normal file
115
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
// 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_replace_params_with_struct.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
|
||||
default;
|
||||
|
||||
void FuzzerPassReplaceParamsWithStruct::Apply() {
|
||||
for (const auto& function : *GetIRContext()->module()) {
|
||||
auto params =
|
||||
fuzzerutil::GetParameters(GetIRContext(), function.result_id());
|
||||
|
||||
if (params.empty() || fuzzerutil::FunctionIsEntryPoint(
|
||||
GetIRContext(), function.result_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> parameter_index(params.size());
|
||||
std::iota(parameter_index.begin(), parameter_index.end(), 0);
|
||||
|
||||
// Remove the indices of unsupported parameters.
|
||||
auto new_end = std::remove_if(
|
||||
parameter_index.begin(), parameter_index.end(),
|
||||
[this, ¶ms](uint32_t index) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(params[index]->type_id());
|
||||
assert(type && "Parameter has invalid type");
|
||||
return !TransformationReplaceParamsWithStruct::
|
||||
IsParameterTypeSupported(*type);
|
||||
});
|
||||
|
||||
// std::remove_if changes the vector so that removed elements are placed at
|
||||
// the end (i.e. [new_end, parameter_index.end()) is a range of removed
|
||||
// elements). However, the size of the vector is not changed so we need to
|
||||
// change that explicitly by popping those elements from the vector.
|
||||
parameter_index.erase(new_end, parameter_index.end());
|
||||
|
||||
if (parameter_index.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Select |num_replaced_params| parameters at random. We shuffle the vector
|
||||
// of indices for randomization and shrink it to select first
|
||||
// |num_replaced_params| parameters.
|
||||
auto num_replaced_params = std::min<size_t>(
|
||||
parameter_index.size(),
|
||||
GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct(
|
||||
static_cast<uint32_t>(params.size())));
|
||||
|
||||
GetFuzzerContext()->Shuffle(¶meter_index);
|
||||
parameter_index.resize(num_replaced_params);
|
||||
|
||||
// Make sure OpTypeStruct exists in the module.
|
||||
std::vector<uint32_t> component_type_ids;
|
||||
for (auto index : parameter_index) {
|
||||
component_type_ids.push_back(params[index]->type_id());
|
||||
}
|
||||
|
||||
FindOrCreateStructType(component_type_ids);
|
||||
|
||||
// Map parameters' indices to parameters' ids.
|
||||
std::vector<uint32_t> parameter_id;
|
||||
for (auto index : parameter_index) {
|
||||
parameter_id.push_back(params[index]->result_id());
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
|
||||
for (const auto* inst :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
caller_id_to_fresh_id[inst->result_id()] =
|
||||
GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationReplaceParamsWithStruct(
|
||||
parameter_id, GetFuzzerContext()->GetFreshId(),
|
||||
GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Iterates over all functions in the module and randomly decides for each one
|
||||
// whether to replace a subset of its parameters with a struct value.
|
||||
class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassReplaceParamsWithStruct(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassReplaceParamsWithStruct() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
|
383
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
383
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@ -23,6 +23,25 @@ namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace fuzzerutil {
|
||||
namespace {
|
||||
|
||||
uint32_t MaybeGetOpConstant(opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t type_id, bool is_irrelevant) {
|
||||
for (const auto& inst : ir_context->types_values()) {
|
||||
if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
|
||||
inst.GetInOperand(0).words == words &&
|
||||
transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
inst.result_id()) == is_irrelevant) {
|
||||
return inst.result_id();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsFreshId(opt::IRContext* context, uint32_t id) {
|
||||
return !context->get_def_use_mgr()->GetDef(id);
|
||||
@ -114,7 +133,7 @@ bool PhiIdsOkForNewEdge(
|
||||
|
||||
void AddUnreachableEdgeAndUpdateOpPhis(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
|
||||
bool condition_value,
|
||||
uint32_t bool_id,
|
||||
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
|
||||
assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
|
||||
"Precondition on phi_ids is not satisfied");
|
||||
@ -122,10 +141,13 @@ void AddUnreachableEdgeAndUpdateOpPhis(
|
||||
"Precondition on terminator of bb_from is not satisfied");
|
||||
|
||||
// Get the id of the boolean constant to be used as the condition.
|
||||
uint32_t bool_id = MaybeGetBoolConstant(context, condition_value);
|
||||
assert(
|
||||
bool_id &&
|
||||
"Precondition that condition value must be available is not satisfied");
|
||||
auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id);
|
||||
assert(condition_inst &&
|
||||
(condition_inst->opcode() == SpvOpConstantTrue ||
|
||||
condition_inst->opcode() == SpvOpConstantFalse) &&
|
||||
"|bool_id| is invalid");
|
||||
|
||||
auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
|
||||
|
||||
const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
|
||||
auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
|
||||
@ -226,7 +248,9 @@ bool CanInsertOpcodeBeforeInstruction(
|
||||
return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
|
||||
}
|
||||
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
opt::Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpSampledImage) {
|
||||
// The SPIR-V data rules say that only very specific instructions may
|
||||
// may consume the result id of an OpSampledImage, and this excludes the
|
||||
@ -237,6 +261,11 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
// We can only make a synonym of an instruction that generates an id.
|
||||
return false;
|
||||
}
|
||||
if (transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
inst->result_id())) {
|
||||
// An irrelevant id can't be a synonym of anything.
|
||||
return false;
|
||||
}
|
||||
if (!inst->type_id()) {
|
||||
// We can only make a synonym of an instruction that has a type.
|
||||
return false;
|
||||
@ -706,6 +735,101 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||
uint32_t function_id) {
|
||||
assert(FindFunction(ir_context, function_id) &&
|
||||
"|function_id| is not a result id of a function");
|
||||
|
||||
std::vector<opt::Instruction*> result;
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
function_id, [&result, function_id](opt::Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpFunctionCall &&
|
||||
inst->GetSingleWordInOperand(0) == function_id) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||
uint32_t param_id) {
|
||||
auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
|
||||
assert(param_inst && "Parameter id is invalid");
|
||||
|
||||
for (auto& function : *ir_context->module()) {
|
||||
if (InstructionIsFunctionParameter(param_inst, &function)) {
|
||||
return &function;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
|
||||
uint32_t new_function_type_result_id,
|
||||
uint32_t return_type_id,
|
||||
const std::vector<uint32_t>& parameter_type_ids) {
|
||||
// Check some initial constraints.
|
||||
assert(ir_context->get_type_mgr()->GetType(return_type_id) &&
|
||||
"Return type is invalid");
|
||||
for (auto id : parameter_type_ids) {
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(id);
|
||||
(void)type; // Make compilers happy in release mode.
|
||||
// Parameters can't be OpTypeVoid.
|
||||
assert(type && !type->AsVoid() && "Parameter has invalid type");
|
||||
}
|
||||
|
||||
auto* function = FindFunction(ir_context, function_id);
|
||||
assert(function && "|function_id| is invalid");
|
||||
|
||||
auto* old_function_type = GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function has invalid type");
|
||||
|
||||
std::vector<uint32_t> operand_ids = {return_type_id};
|
||||
operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
|
||||
parameter_type_ids.end());
|
||||
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
|
||||
FindFunctionType(ir_context, operand_ids) == 0) {
|
||||
// We can change |old_function_type| only if it's used once in the module
|
||||
// and we are certain we won't create a duplicate as a result of the change.
|
||||
|
||||
// Update |old_function_type| in-place.
|
||||
opt::Instruction::OperandList operands;
|
||||
for (auto id : operand_ids) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
|
||||
}
|
||||
|
||||
old_function_type->SetInOperands(std::move(operands));
|
||||
|
||||
// |operands| may depend on result ids defined below the |old_function_type|
|
||||
// in the module.
|
||||
old_function_type->RemoveFromList();
|
||||
ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
|
||||
return old_function_type->result_id();
|
||||
} else {
|
||||
// We can't modify the |old_function_type| so we have to either use an
|
||||
// existing one or create a new one.
|
||||
auto type_id = FindOrCreateFunctionType(
|
||||
ir_context, new_function_type_result_id, operand_ids);
|
||||
|
||||
if (type_id != old_function_type->result_id()) {
|
||||
function->DefInst().SetInOperand(1, {type_id});
|
||||
|
||||
// DefUseManager hasn't been updated yet, so if the following condition is
|
||||
// true, then |old_function_type| will have no users when this function
|
||||
// returns. We might as well remove it.
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
||||
old_function_type->RemoveFromList();
|
||||
delete old_function_type;
|
||||
}
|
||||
}
|
||||
|
||||
return type_id;
|
||||
}
|
||||
}
|
||||
|
||||
void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& type_ids) {
|
||||
assert(result_id != 0 && "Result id can't be 0");
|
||||
@ -755,6 +879,11 @@ uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) {
|
||||
return ir_context->get_type_mgr()->GetId(&type);
|
||||
}
|
||||
|
||||
uint32_t MaybeGetBoolType(opt::IRContext* ir_context) {
|
||||
opt::analysis::Bool type;
|
||||
return ir_context->get_type_mgr()->GetId(&type);
|
||||
}
|
||||
|
||||
uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
|
||||
uint32_t component_type_id,
|
||||
uint32_t element_count) {
|
||||
@ -786,15 +915,18 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context,
|
||||
return ir_context->get_type_mgr()->GetId(&type);
|
||||
}
|
||||
|
||||
uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
uint32_t scalar_or_composite_type_id) {
|
||||
uint32_t MaybeGetZeroConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
|
||||
const auto* type =
|
||||
ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
|
||||
assert(type && "|scalar_or_composite_type_id| is invalid");
|
||||
|
||||
switch (type->kind()) {
|
||||
case opt::analysis::Type::kBool:
|
||||
return MaybeGetBoolConstant(ir_context, false);
|
||||
return MaybeGetBoolConstant(ir_context, transformation_context, false,
|
||||
is_irrelevant);
|
||||
case opt::analysis::Type::kFloat:
|
||||
case opt::analysis::Type::kInteger: {
|
||||
std::vector<uint32_t> words = {0};
|
||||
@ -803,8 +935,8 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
words.push_back(0);
|
||||
}
|
||||
|
||||
return MaybeGetScalarConstant(ir_context, words,
|
||||
scalar_or_composite_type_id);
|
||||
return MaybeGetScalarConstant(ir_context, transformation_context, words,
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
case opt::analysis::Type::kStruct: {
|
||||
std::vector<uint32_t> component_ids;
|
||||
@ -813,7 +945,16 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
ir_context->get_type_mgr()->GetId(component_type);
|
||||
assert(component_type_id && "Component type is invalid");
|
||||
|
||||
auto component_id = MaybeGetZeroConstant(ir_context, component_type_id);
|
||||
auto component_id =
|
||||
MaybeGetZeroConstant(ir_context, transformation_context,
|
||||
component_type_id, is_irrelevant);
|
||||
if (component_id == 0 && is_irrelevant) {
|
||||
// Irrelevant constants can use either relevant or irrelevant
|
||||
// constituents.
|
||||
component_id = MaybeGetZeroConstant(
|
||||
ir_context, transformation_context, component_type_id, false);
|
||||
}
|
||||
|
||||
if (component_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -821,8 +962,9 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
component_ids.push_back(component_id);
|
||||
}
|
||||
|
||||
return MaybeGetCompositeConstant(ir_context, component_ids,
|
||||
scalar_or_composite_type_id);
|
||||
return MaybeGetCompositeConstant(
|
||||
ir_context, transformation_context, component_ids,
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
case opt::analysis::Type::kMatrix:
|
||||
case opt::analysis::Type::kVector: {
|
||||
@ -833,39 +975,56 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
ir_context->get_type_mgr()->GetId(component_type);
|
||||
assert(component_type_id && "Component type is invalid");
|
||||
|
||||
if (auto component_id =
|
||||
MaybeGetZeroConstant(ir_context, component_type_id)) {
|
||||
auto component_count = type->AsVector()
|
||||
? type->AsVector()->element_count()
|
||||
: type->AsMatrix()->element_count();
|
||||
return MaybeGetCompositeConstant(
|
||||
ir_context, std::vector<uint32_t>(component_count, component_id),
|
||||
scalar_or_composite_type_id);
|
||||
auto component_id = MaybeGetZeroConstant(
|
||||
ir_context, transformation_context, component_type_id, is_irrelevant);
|
||||
|
||||
if (component_id == 0 && is_irrelevant) {
|
||||
// Irrelevant constants can use either relevant or irrelevant
|
||||
// constituents.
|
||||
component_id = MaybeGetZeroConstant(ir_context, transformation_context,
|
||||
component_type_id, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (component_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto component_count = type->AsVector()
|
||||
? type->AsVector()->element_count()
|
||||
: type->AsMatrix()->element_count();
|
||||
return MaybeGetCompositeConstant(
|
||||
ir_context, transformation_context,
|
||||
std::vector<uint32_t>(component_count, component_id),
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
case opt::analysis::Type::kArray: {
|
||||
auto component_type_id =
|
||||
ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
|
||||
assert(component_type_id && "Component type is invalid");
|
||||
|
||||
if (auto component_id =
|
||||
MaybeGetZeroConstant(ir_context, component_type_id)) {
|
||||
auto type_id = ir_context->get_type_mgr()->GetId(type);
|
||||
assert(type_id && "|type| is invalid");
|
||||
auto component_id = MaybeGetZeroConstant(
|
||||
ir_context, transformation_context, component_type_id, is_irrelevant);
|
||||
|
||||
const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
|
||||
assert(type_inst && "Array's type id is invalid");
|
||||
|
||||
return MaybeGetCompositeConstant(
|
||||
ir_context,
|
||||
std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
|
||||
component_id),
|
||||
scalar_or_composite_type_id);
|
||||
if (component_id == 0 && is_irrelevant) {
|
||||
component_id = MaybeGetZeroConstant(ir_context, transformation_context,
|
||||
component_type_id, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (component_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto type_id = ir_context->get_type_mgr()->GetId(type);
|
||||
assert(type_id && "|type| is invalid");
|
||||
|
||||
const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
|
||||
assert(type_inst && "Array's type id is invalid");
|
||||
|
||||
return MaybeGetCompositeConstant(
|
||||
ir_context, transformation_context,
|
||||
std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
|
||||
component_id),
|
||||
scalar_or_composite_type_id, is_irrelevant);
|
||||
}
|
||||
default:
|
||||
assert(false && "Type is not supported");
|
||||
@ -873,110 +1032,106 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t scalar_type_id) {
|
||||
uint32_t MaybeGetScalarConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t scalar_type_id,
|
||||
bool is_irrelevant) {
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
|
||||
assert(type && "|scalar_type_id| is invalid");
|
||||
|
||||
if (const auto* int_type = type->AsInteger()) {
|
||||
return MaybeGetIntegerConstant(ir_context, words, int_type->width(),
|
||||
int_type->IsSigned());
|
||||
return MaybeGetIntegerConstant(ir_context, transformation_context, words,
|
||||
int_type->width(), int_type->IsSigned(),
|
||||
is_irrelevant);
|
||||
} else if (const auto* float_type = type->AsFloat()) {
|
||||
return MaybeGetFloatConstant(ir_context, words, float_type->width());
|
||||
return MaybeGetFloatConstant(ir_context, transformation_context, words,
|
||||
float_type->width(), is_irrelevant);
|
||||
} else {
|
||||
assert(type->AsBool() && words.size() == 1 &&
|
||||
"|scalar_type_id| doesn't represent a scalar type");
|
||||
return MaybeGetBoolConstant(ir_context, words[0]);
|
||||
return MaybeGetBoolConstant(ir_context, transformation_context, words[0],
|
||||
is_irrelevant);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& component_ids,
|
||||
uint32_t composite_type_id) {
|
||||
std::vector<const opt::analysis::Constant*> constants;
|
||||
for (auto id : component_ids) {
|
||||
const auto* component_constant =
|
||||
ir_context->get_constant_mgr()->FindDeclaredConstant(id);
|
||||
assert(component_constant && "|id| is invalid");
|
||||
|
||||
constants.push_back(component_constant);
|
||||
}
|
||||
|
||||
uint32_t MaybeGetCompositeConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
|
||||
bool is_irrelevant) {
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
|
||||
assert(type && "|composite_type_id| is invalid");
|
||||
(void)type; // Make compilers happy in release mode.
|
||||
assert(type &&
|
||||
(type->AsArray() || type->AsStruct() || type->AsVector() ||
|
||||
type->AsMatrix()) &&
|
||||
"|composite_type_id| is invalid");
|
||||
|
||||
std::unique_ptr<opt::analysis::Constant> composite_constant;
|
||||
switch (type->kind()) {
|
||||
case opt::analysis::Type::kStruct:
|
||||
composite_constant = MakeUnique<opt::analysis::StructConstant>(
|
||||
type->AsStruct(), std::move(constants));
|
||||
break;
|
||||
case opt::analysis::Type::kVector:
|
||||
composite_constant = MakeUnique<opt::analysis::VectorConstant>(
|
||||
type->AsVector(), std::move(constants));
|
||||
break;
|
||||
case opt::analysis::Type::kMatrix:
|
||||
composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
|
||||
type->AsMatrix(), std::move(constants));
|
||||
break;
|
||||
case opt::analysis::Type::kArray:
|
||||
composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
|
||||
type->AsArray(), std::move(constants));
|
||||
break;
|
||||
default:
|
||||
assert(false &&
|
||||
"|composite_type_id| is not a result id of a composite type");
|
||||
return 0;
|
||||
for (const auto& inst : ir_context->types_values()) {
|
||||
if (inst.opcode() == SpvOpConstantComposite &&
|
||||
inst.type_id() == composite_type_id &&
|
||||
transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
inst.result_id()) == is_irrelevant &&
|
||||
inst.NumInOperands() == component_ids.size()) {
|
||||
bool is_match = true;
|
||||
|
||||
for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
|
||||
if (inst.GetSingleWordInOperand(i) != component_ids[i]) {
|
||||
is_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_match) {
|
||||
return inst.result_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ir_context->get_constant_mgr()->FindDeclaredConstant(
|
||||
composite_constant.get(), composite_type_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t width, bool is_signed) {
|
||||
auto type_id = MaybeGetIntegerType(ir_context, width, is_signed);
|
||||
if (!type_id) {
|
||||
return 0;
|
||||
uint32_t MaybeGetIntegerConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
|
||||
bool is_irrelevant) {
|
||||
if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
|
||||
return MaybeGetOpConstant(ir_context, transformation_context, words,
|
||||
type_id, is_irrelevant);
|
||||
}
|
||||
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(type_id);
|
||||
assert(type && "|type_id| is invalid");
|
||||
|
||||
opt::analysis::IntConstant constant(type->AsInteger(), words);
|
||||
return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
|
||||
type_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t width) {
|
||||
auto type_id = MaybeGetFloatType(ir_context, width);
|
||||
if (!type_id) {
|
||||
return 0;
|
||||
uint32_t MaybeGetFloatConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
|
||||
if (auto type_id = MaybeGetFloatType(ir_context, width)) {
|
||||
return MaybeGetOpConstant(ir_context, transformation_context, words,
|
||||
type_id, is_irrelevant);
|
||||
}
|
||||
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(type_id);
|
||||
assert(type && "|type_id| is invalid");
|
||||
|
||||
opt::analysis::FloatConstant constant(type->AsFloat(), words);
|
||||
return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
|
||||
type_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value) {
|
||||
opt::analysis::Bool bool_type;
|
||||
auto registered_bool_type =
|
||||
context->get_type_mgr()->GetRegisteredType(&bool_type);
|
||||
if (!registered_bool_type) {
|
||||
return 0;
|
||||
uint32_t MaybeGetBoolConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context, bool value,
|
||||
bool is_irrelevant) {
|
||||
if (auto type_id = MaybeGetBoolType(ir_context)) {
|
||||
for (const auto& inst : ir_context->types_values()) {
|
||||
if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
|
||||
inst.type_id() == type_id &&
|
||||
transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
inst.result_id()) == is_irrelevant) {
|
||||
return inst.result_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
|
||||
value);
|
||||
return context->get_constant_mgr()->FindDeclaredConstant(
|
||||
&bool_constant, context->get_type_mgr()->GetId(&bool_type));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
|
106
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
106
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@ -53,15 +53,17 @@ bool PhiIdsOkForNewEdge(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
|
||||
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
|
||||
|
||||
// Requires that a boolean constant with value |condition_value| is available,
|
||||
// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
|
||||
// bb_from ends with "OpBranch %some_block". Turns OpBranch into
|
||||
// "OpBranchConditional |condition_value| ...", such that control will branch
|
||||
// to %some_block, with |bb_to| being the unreachable alternative. Updates
|
||||
// OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
|
||||
// Requires that |bool_id| is a valid result id of either OpConstantTrue or
|
||||
// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
|
||||
// holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch
|
||||
// into "OpBranchConditional |condition_value| ...", such that control will
|
||||
// branch to %some_block, with |bb_to| being the unreachable alternative.
|
||||
// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is
|
||||
// valid. |condition_value| above is equal to |true| if |bool_id| is a result id
|
||||
// of an OpConstantTrue instruction.
|
||||
void AddUnreachableEdgeAndUpdateOpPhis(
|
||||
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
|
||||
bool condition_value,
|
||||
uint32_t bool_id,
|
||||
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
|
||||
|
||||
// Returns true if and only if |loop_header_id| is a loop header and
|
||||
@ -91,7 +93,11 @@ bool CanInsertOpcodeBeforeInstruction(
|
||||
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
|
||||
|
||||
// Determines whether it is OK to make a synonym of |inst|.
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
|
||||
// |transformation_context| is used to verify that the result id of |inst|
|
||||
// does not participate in IdIsIrrelevant fact.
|
||||
bool CanMakeSynonymOf(opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
opt::Instruction* inst);
|
||||
|
||||
// Determines whether the given type is a composite; that is: an array, matrix,
|
||||
// struct or vector.
|
||||
@ -275,6 +281,29 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
uint32_t function_id);
|
||||
|
||||
// Returns all OpFunctionCall instructions that call a function with result id
|
||||
// |function_id|.
|
||||
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||
uint32_t function_id);
|
||||
|
||||
// Returns a function that contains OpFunctionParameter instruction with result
|
||||
// id |param_id|. Returns nullptr if the module has no such function.
|
||||
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||
uint32_t param_id);
|
||||
|
||||
// Changes the type of function |function_id| so that its return type is
|
||||
// |return_type_id| and its parameters' types are |parameter_type_ids|. If a
|
||||
// suitable function type already exists in the module, it is used, otherwise
|
||||
// |new_function_type_result_id| is used as the result id of a suitable new
|
||||
// function type instruction. If the old type of the function doesn't have any
|
||||
// more users, it is removed from the module. Returns the result id of the
|
||||
// OpTypeFunction instruction that is used as a type of the function with
|
||||
// |function_id|.
|
||||
uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
|
||||
uint32_t new_function_type_result_id,
|
||||
uint32_t return_type_id,
|
||||
const std::vector<uint32_t>& parameter_type_ids);
|
||||
|
||||
// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
|
||||
// empty. It may not contain result ids of OpTypeFunction instructions.
|
||||
// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
|
||||
@ -302,6 +331,10 @@ uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
|
||||
// otherwise.
|
||||
uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width);
|
||||
|
||||
// Returns a result id of an OpTypeBool instruction if present. Returns 0
|
||||
// otherwise.
|
||||
uint32_t MaybeGetBoolType(opt::IRContext* ir_context);
|
||||
|
||||
// Returns a result id of an OpTypeVector instruction if present. Returns 0
|
||||
// otherwise. |component_type_id| must be a valid result id of an OpTypeInt,
|
||||
// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be
|
||||
@ -323,39 +356,60 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context,
|
||||
// Every component of the composite constant is looked up by calling this
|
||||
// function with the type id of that component.
|
||||
// Returns 0 if no such instruction is present in the module.
|
||||
uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
|
||||
uint32_t scalar_or_composite_type_id);
|
||||
// The returned id either participates in IdIsIrrelevant fact or not, depending
|
||||
// on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetZeroConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
uint32_t scalar_or_composite_type_id, bool is_irrelevant);
|
||||
|
||||
// Returns the result id of an OpConstant instruction. |scalar_type_id| must be
|
||||
// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
|
||||
// instruction is present in the module.
|
||||
uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t scalar_type_id);
|
||||
// instruction is present in the module. The returned id either participates in
|
||||
// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetScalarConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t scalar_type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the result id of an OpConstantComposite instruction.
|
||||
// |composite_type_id| must be a result id of a composite type (i.e. vector,
|
||||
// matrix, struct or array). Returns 0 if no such instruction is present in the
|
||||
// module.
|
||||
uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& component_ids,
|
||||
uint32_t composite_type_id);
|
||||
// module. The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetCompositeConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the result id of an OpConstant instruction of integral type.
|
||||
// Returns 0 if no such instruction or type is present in the module.
|
||||
uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t width, bool is_signed);
|
||||
// The returned id either participates in IdIsIrrelevant fact or not, depending
|
||||
// on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetIntegerConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the result id of an OpConstant instruction of floating-point type.
|
||||
// Returns 0 if no such instruction or type is present in the module.
|
||||
uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
|
||||
const std::vector<uint32_t>& words,
|
||||
uint32_t width);
|
||||
// The returned id either participates in IdIsIrrelevant fact or not, depending
|
||||
// on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetFloatConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant);
|
||||
|
||||
// Returns the id of a boolean constant with value |value| if it exists in the
|
||||
// module, or 0 otherwise.
|
||||
uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value);
|
||||
// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant
|
||||
// fact or not, depending on the |is_irrelevant| parameter.
|
||||
uint32_t MaybeGetBoolConstant(
|
||||
opt::IRContext* context,
|
||||
const TransformationContext& transformation_context, bool value,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Creates a new OpTypeInt instruction in the module. Updates module's id bound
|
||||
// to accommodate for |result_id|.
|
||||
|
@ -170,6 +170,7 @@ message Fact {
|
||||
FactFunctionIsLivesafe function_is_livesafe_fact = 4;
|
||||
FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
|
||||
FactIdEquation id_equation_fact = 6;
|
||||
FactIdIsIrrelevant id_is_irrelevant = 7;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +183,7 @@ message FactBlockIsDead {
|
||||
// can be made to this block.
|
||||
|
||||
uint32 block_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message FactConstantUniform {
|
||||
@ -222,6 +224,7 @@ message FactFunctionIsLivesafe {
|
||||
// functions.
|
||||
|
||||
uint32 function_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message FactIdEquation {
|
||||
@ -249,6 +252,18 @@ message FactIdEquation {
|
||||
|
||||
}
|
||||
|
||||
message FactIdIsIrrelevant {
|
||||
|
||||
// Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
|
||||
// change the semantics of the module). This implies that a use of this id
|
||||
// can later be replaced with some other id of the same type, or the
|
||||
// definition of |result_id| can be changed so that it yields a different value.
|
||||
|
||||
// An irrelevant id.
|
||||
uint32 result_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message FactPointeeValueIsIrrelevant {
|
||||
|
||||
// Records the fact that value of the data pointed to by a pointer id does
|
||||
@ -258,6 +273,7 @@ message FactPointeeValueIsIrrelevant {
|
||||
|
||||
// A result id of pointer type
|
||||
uint32 pointer_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message AccessChainClampingInfo {
|
||||
@ -386,6 +402,10 @@ message Transformation {
|
||||
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
|
||||
TransformationRecordSynonymousConstants record_synonymous_constants = 56;
|
||||
TransformationAddSynonym add_synonym = 57;
|
||||
TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
|
||||
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
|
||||
TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
|
||||
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@ -415,15 +435,21 @@ message TransformationAddConstantBoolean {
|
||||
|
||||
// Supports adding the constants true and false to a module, which may be
|
||||
// necessary in order to enable other transformations if they are not present.
|
||||
// Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
|
||||
|
||||
uint32 fresh_id = 1;
|
||||
bool is_true = 2;
|
||||
|
||||
// If the constant should be marked as irrelevant.
|
||||
bool is_irrelevant = 3;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddConstantComposite {
|
||||
|
||||
// Adds a constant of the given composite type to the module.
|
||||
// Also, creates an IdIsIrrelevant fact about |fresh_id| if
|
||||
// |is_irrelevant| is true.
|
||||
|
||||
// Fresh id for the composite
|
||||
uint32 fresh_id = 1;
|
||||
@ -434,6 +460,9 @@ message TransformationAddConstantComposite {
|
||||
// Constituent ids for the composite
|
||||
repeated uint32 constituent_id = 3;
|
||||
|
||||
// If the constant should be marked as irrelevant.
|
||||
bool is_irrelevant = 4;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddConstantNull {
|
||||
@ -451,6 +480,8 @@ message TransformationAddConstantNull {
|
||||
message TransformationAddConstantScalar {
|
||||
|
||||
// Adds a constant of the given scalar type.
|
||||
// Also, creates an IdIsIrrelevant fact about
|
||||
// |fresh_id| if |is_irrelevant| is true.
|
||||
|
||||
// Id for the constant
|
||||
uint32 fresh_id = 1;
|
||||
@ -461,6 +492,9 @@ message TransformationAddConstantScalar {
|
||||
// Value of the constant
|
||||
repeated uint32 word = 3;
|
||||
|
||||
// If the constant should be marked as irrelevant.
|
||||
bool is_irrelevant = 4;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddCopyMemory {
|
||||
@ -687,6 +721,15 @@ message TransformationAddParameter {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddRelaxedDecoration {
|
||||
|
||||
// Applies OpDecorate RelaxedPrecision to the given result id
|
||||
|
||||
// Result id to be decorated
|
||||
uint32 result_id = 1;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddSpecConstantOp {
|
||||
|
||||
// Adds OpSpecConstantOp into the module.
|
||||
@ -1233,6 +1276,39 @@ message TransformationReplaceConstantWithUniform {
|
||||
|
||||
}
|
||||
|
||||
message TransformationReplaceCopyMemoryWithLoadStore {
|
||||
|
||||
// A transformation that replaces instructions OpCopyMemory with loading
|
||||
// the source variable to an intermediate value and storing this value into the
|
||||
// target variable of the original OpCopyMemory instruction.
|
||||
|
||||
// The intermediate value.
|
||||
uint32 fresh_id = 1;
|
||||
|
||||
// The instruction descriptor to OpCopyMemory. It is necessary, because
|
||||
// OpCopyMemory doesn't have a result id.
|
||||
InstructionDescriptor copy_memory_instruction_descriptor = 2;
|
||||
}
|
||||
|
||||
message TransformationReplaceCopyObjectWithStoreLoad {
|
||||
|
||||
// A transformation that replaces instruction OpCopyObject with
|
||||
// storing into a new variable and immediately loading from this
|
||||
// variable to |result_id| of the original OpCopyObject instruction.
|
||||
|
||||
// The result id of initial OpCopyObject instruction
|
||||
uint32 copy_object_result_id = 1;
|
||||
|
||||
// A fresh id for the variable to be stored to.
|
||||
uint32 fresh_variable_id = 2;
|
||||
|
||||
// The variable storage class (Function or Private).
|
||||
uint32 variable_storage_class = 3;
|
||||
|
||||
// Constant to initialize the variable with.
|
||||
uint32 variable_initializer_id = 4;
|
||||
}
|
||||
|
||||
message TransformationReplaceIdWithSynonym {
|
||||
|
||||
// Replaces a use of an id with an id that is known to be synonymous, e.g.
|
||||
@ -1273,6 +1349,31 @@ message TransformationReplaceLinearAlgebraInstruction {
|
||||
|
||||
}
|
||||
|
||||
message TransformationReplaceParamsWithStruct {
|
||||
|
||||
// Replaces parameters of the function with a struct containing
|
||||
// values of those parameters.
|
||||
|
||||
// Result ids of parameters to replace.
|
||||
repeated uint32 parameter_id = 1;
|
||||
|
||||
// Fresh id for a new function type. This might be unused if the required type
|
||||
// already exists in the module or if we can change the old type.
|
||||
uint32 fresh_function_type_id = 2;
|
||||
|
||||
// Fresh id for a new struct function parameter to be used as a replacement.
|
||||
uint32 fresh_parameter_id = 3;
|
||||
|
||||
// Fresh ids for struct objects containing values of replaced parameters.
|
||||
// This map contains a fresh id for at least every result id of a relevant
|
||||
// OpFunctionCall instruction.
|
||||
//
|
||||
// While maps are not fully deterministic, the way this map is used does not
|
||||
// exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
|
||||
map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
|
||||
|
||||
}
|
||||
|
||||
message TransformationSetFunctionControl {
|
||||
|
||||
// A transformation that sets the function control operand of an OpFunction
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
#include "source/fuzz/transformation_add_relaxed_decoration.h"
|
||||
#include "source/fuzz/transformation_add_spec_constant_op.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
#include "source/fuzz/transformation_add_type_array.h"
|
||||
@ -61,9 +62,12 @@
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
|
||||
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
|
||||
#include "source/fuzz/transformation_replace_parameter_with_global.h"
|
||||
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||
#include "source/fuzz/transformation_set_function_control.h"
|
||||
#include "source/fuzz/transformation_set_loop_control.h"
|
||||
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
||||
@ -128,6 +132,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
message.add_no_contraction_decoration());
|
||||
case protobufs::Transformation::TransformationCase::kAddParameter:
|
||||
return MakeUnique<TransformationAddParameter>(message.add_parameter());
|
||||
case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration:
|
||||
return MakeUnique<TransformationAddRelaxedDecoration>(
|
||||
message.add_relaxed_decoration());
|
||||
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
|
||||
return MakeUnique<TransformationAddSpecConstantOp>(
|
||||
message.add_spec_constant_op());
|
||||
@ -211,6 +218,14 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kReplaceConstantWithUniform:
|
||||
return MakeUnique<TransformationReplaceConstantWithUniform>(
|
||||
message.replace_constant_with_uniform());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceCopyMemoryWithLoadStore:
|
||||
return MakeUnique<TransformationReplaceCopyMemoryWithLoadStore>(
|
||||
message.replace_copy_memory_with_load_store());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceCopyObjectWithStoreLoad:
|
||||
return MakeUnique<TransformationReplaceCopyObjectWithStoreLoad>(
|
||||
message.replace_copy_object_with_store_load());
|
||||
case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
|
||||
return MakeUnique<TransformationReplaceIdWithSynonym>(
|
||||
message.replace_id_with_synonym());
|
||||
@ -218,6 +233,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kReplaceLinearAlgebraInstruction:
|
||||
return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
|
||||
message.replace_linear_algebra_instruction());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceParamsWithStruct:
|
||||
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
||||
message.replace_params_with_struct());
|
||||
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
|
||||
return MakeUnique<TransformationSetFunctionControl>(
|
||||
message.set_function_control());
|
||||
|
@ -25,34 +25,36 @@ TransformationAddConstantBoolean::TransformationAddConstantBoolean(
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddConstantBoolean::TransformationAddConstantBoolean(
|
||||
uint32_t fresh_id, bool is_true) {
|
||||
uint32_t fresh_id, bool is_true, bool is_irrelevant) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_is_true(is_true);
|
||||
message_.set_is_irrelevant(is_irrelevant);
|
||||
}
|
||||
|
||||
bool TransformationAddConstantBoolean::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::analysis::Bool bool_type;
|
||||
if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
|
||||
// No OpTypeBool is present.
|
||||
return false;
|
||||
}
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
|
||||
return fuzzerutil::MaybeGetBoolType(ir_context) != 0 &&
|
||||
fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
|
||||
}
|
||||
|
||||
void TransformationAddConstantBoolean::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::analysis::Bool bool_type;
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Add the boolean constant to the module, ensuring the module's id bound is
|
||||
// high enough.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
ir_context->module()->AddGlobalValue(
|
||||
message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
|
||||
message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
|
||||
message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context));
|
||||
// We have added an instruction to the module, so need to be careful about the
|
||||
// validity of existing analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
|
||||
if (message_.is_irrelevant()) {
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {
|
||||
|
@ -28,7 +28,8 @@ class TransformationAddConstantBoolean : public Transformation {
|
||||
explicit TransformationAddConstantBoolean(
|
||||
const protobufs::TransformationAddConstantBoolean& message);
|
||||
|
||||
TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
|
||||
TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true,
|
||||
bool is_irrelevant);
|
||||
|
||||
// - |message_.fresh_id| must not be used by the module.
|
||||
// - The module must already contain OpTypeBool.
|
||||
@ -38,6 +39,8 @@ class TransformationAddConstantBoolean : public Transformation {
|
||||
|
||||
// - Adds OpConstantTrue (OpConstantFalse) to the module with id
|
||||
// |message_.fresh_id| if |message_.is_true| holds (does not hold).
|
||||
// - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant|
|
||||
// is true.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
@ -28,9 +28,10 @@ TransformationAddConstantComposite::TransformationAddConstantComposite(
|
||||
|
||||
TransformationAddConstantComposite::TransformationAddConstantComposite(
|
||||
uint32_t fresh_id, uint32_t type_id,
|
||||
const std::vector<uint32_t>& constituent_ids) {
|
||||
const std::vector<uint32_t>& constituent_ids, bool is_irrelevant) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_type_id(type_id);
|
||||
message_.set_is_irrelevant(is_irrelevant);
|
||||
for (auto constituent_id : constituent_ids) {
|
||||
message_.add_constituent_id(constituent_id);
|
||||
}
|
||||
@ -104,7 +105,8 @@ bool TransformationAddConstantComposite::IsApplicable(
|
||||
}
|
||||
|
||||
void TransformationAddConstantComposite::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto constituent_id : message_.constituent_id()) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
|
||||
@ -117,6 +119,11 @@ void TransformationAddConstantComposite::Apply(
|
||||
// validity of existing analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
|
||||
if (message_.is_irrelevant()) {
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantComposite::ToMessage()
|
||||
|
@ -32,7 +32,7 @@ class TransformationAddConstantComposite : public Transformation {
|
||||
|
||||
TransformationAddConstantComposite(
|
||||
uint32_t fresh_id, uint32_t type_id,
|
||||
const std::vector<uint32_t>& constituent_ids);
|
||||
const std::vector<uint32_t>& constituent_ids, bool is_irrelevant);
|
||||
|
||||
// - |message_.fresh_id| must be a fresh id
|
||||
// - |message_.type_id| must be the id of a composite type
|
||||
@ -42,9 +42,11 @@ class TransformationAddConstantComposite : public Transformation {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds an OpConstantComposite instruction defining a constant of type
|
||||
// |message_.type_id|, using |message_.constituent_id| as constituents, with
|
||||
// result id |message_.fresh_id|.
|
||||
// - Adds an OpConstantComposite instruction defining a constant of type
|
||||
// |message_.type_id|, using |message_.constituent_id| as constituents, with
|
||||
// result id |message_.fresh_id|.
|
||||
// - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is
|
||||
// true.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
@ -24,9 +24,11 @@ TransformationAddConstantScalar::TransformationAddConstantScalar(
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddConstantScalar::TransformationAddConstantScalar(
|
||||
uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
|
||||
uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words,
|
||||
bool is_irrelevant) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_type_id(type_id);
|
||||
message_.set_is_irrelevant(is_irrelevant);
|
||||
for (auto word : words) {
|
||||
message_.add_word(word);
|
||||
}
|
||||
@ -60,7 +62,8 @@ bool TransformationAddConstantScalar::IsApplicable(
|
||||
}
|
||||
|
||||
void TransformationAddConstantScalar::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
opt::Instruction::OperandList operand_list;
|
||||
for (auto word : message_.word()) {
|
||||
operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
|
||||
@ -75,6 +78,11 @@ void TransformationAddConstantScalar::Apply(
|
||||
// validity of existing analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
|
||||
if (message_.is_irrelevant()) {
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {
|
||||
|
@ -31,7 +31,8 @@ class TransformationAddConstantScalar : public Transformation {
|
||||
const protobufs::TransformationAddConstantScalar& message);
|
||||
|
||||
TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
|
||||
std::vector<uint32_t> words);
|
||||
const std::vector<uint32_t>& words,
|
||||
bool is_irrelevant);
|
||||
|
||||
// - |message_.fresh_id| must not be used by the module
|
||||
// - |message_.type_id| must be the id of a floating-point or integer type
|
||||
@ -42,6 +43,7 @@ class TransformationAddConstantScalar : public Transformation {
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds a new OpConstant instruction with the given type and words.
|
||||
// Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
@ -32,7 +32,8 @@ TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
|
||||
}
|
||||
|
||||
bool TransformationAddDeadBlock::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// The new block's id must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
@ -40,8 +41,8 @@ bool TransformationAddDeadBlock::IsApplicable(
|
||||
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
|
||||
message_.condition_value())) {
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.condition_value(), false)) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
return false;
|
||||
@ -92,8 +93,8 @@ void TransformationAddDeadBlock::Apply(
|
||||
existing_block->terminator()->GetSingleWordInOperand(0);
|
||||
|
||||
// Get the id of the boolean value that will be used as the branch condition.
|
||||
auto bool_id =
|
||||
fuzzerutil::MaybeGetBoolConstant(ir_context, message_.condition_value());
|
||||
auto bool_id = fuzzerutil::MaybeGetBoolConstant(
|
||||
ir_context, *transformation_context, message_.condition_value(), false);
|
||||
|
||||
// Make a new block that unconditionally branches to the original successor
|
||||
// block.
|
||||
|
@ -112,8 +112,9 @@ bool TransformationAddDeadBreak::IsApplicable(
|
||||
const TransformationContext& transformation_context) const {
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.break_condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
|
||||
message_.break_condition_value())) {
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.break_condition_value(),
|
||||
false)) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
return false;
|
||||
@ -179,14 +180,15 @@ bool TransformationAddDeadBreak::IsApplicable(
|
||||
// the validator is complete with respect to checking structured control flow
|
||||
// rules.
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
|
||||
ApplyImpl(cloned_context.get());
|
||||
ApplyImpl(cloned_context.get(), transformation_context);
|
||||
return fuzzerutil::IsValid(cloned_context.get(),
|
||||
transformation_context.GetValidatorOptions());
|
||||
}
|
||||
|
||||
void TransformationAddDeadBreak::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
ApplyImpl(ir_context);
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
ApplyImpl(ir_context, *transformation_context);
|
||||
// Invalidate all analyses
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
@ -199,11 +201,14 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
|
||||
}
|
||||
|
||||
void TransformationAddDeadBreak::ApplyImpl(
|
||||
spvtools::opt::IRContext* ir_context) const {
|
||||
spvtools::opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
||||
ir_context, ir_context->cfg()->block(message_.from_block()),
|
||||
ir_context->cfg()->block(message_.to_block()),
|
||||
message_.break_condition_value(), message_.phi_id());
|
||||
fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.break_condition_value(), false),
|
||||
message_.phi_id());
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -75,7 +75,8 @@ class TransformationAddDeadBreak : public Transformation {
|
||||
// module. This is only invoked by 'IsApplicable' after certain basic
|
||||
// applicability checks have been made, ensuring that the invocation of this
|
||||
// method is legal.
|
||||
void ApplyImpl(opt::IRContext* ir_context) const;
|
||||
void ApplyImpl(opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const;
|
||||
|
||||
protobufs::TransformationAddDeadBreak message_;
|
||||
};
|
||||
|
@ -38,8 +38,9 @@ bool TransformationAddDeadContinue::IsApplicable(
|
||||
const TransformationContext& transformation_context) const {
|
||||
// First, we check that a constant with the same value as
|
||||
// |message_.continue_condition_value| is present.
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
|
||||
message_.continue_condition_value())) {
|
||||
if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.continue_condition_value(),
|
||||
false)) {
|
||||
// The required constant is not present, so the transformation cannot be
|
||||
// applied.
|
||||
return false;
|
||||
@ -119,14 +120,15 @@ bool TransformationAddDeadContinue::IsApplicable(
|
||||
// the validator is complete with respect to checking structured control flow
|
||||
// rules.
|
||||
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
|
||||
ApplyImpl(cloned_context.get());
|
||||
ApplyImpl(cloned_context.get(), transformation_context);
|
||||
return fuzzerutil::IsValid(cloned_context.get(),
|
||||
transformation_context.GetValidatorOptions());
|
||||
}
|
||||
|
||||
void TransformationAddDeadContinue::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
ApplyImpl(ir_context);
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
ApplyImpl(ir_context, *transformation_context);
|
||||
// Invalidate all analyses
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
@ -139,7 +141,8 @@ protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
|
||||
}
|
||||
|
||||
void TransformationAddDeadContinue::ApplyImpl(
|
||||
spvtools::opt::IRContext* ir_context) const {
|
||||
spvtools::opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
auto bb_from = ir_context->cfg()->block(message_.from_block());
|
||||
auto continue_block =
|
||||
bb_from->IsLoopHeader()
|
||||
@ -149,7 +152,10 @@ void TransformationAddDeadContinue::ApplyImpl(
|
||||
assert(continue_block && "message_.from_block must be in a loop.");
|
||||
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
|
||||
ir_context, bb_from, ir_context->cfg()->block(continue_block),
|
||||
message_.continue_condition_value(), message_.phi_id());
|
||||
fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.continue_condition_value(),
|
||||
false),
|
||||
message_.phi_id());
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -72,7 +72,8 @@ class TransformationAddDeadContinue : public Transformation {
|
||||
// module. This is only invoked by 'IsApplicable' after certain basic
|
||||
// applicability checks have been made, ensuring that the invocation of this
|
||||
// method is legal.
|
||||
void ApplyImpl(opt::IRContext* ir_context) const;
|
||||
void ApplyImpl(opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const;
|
||||
|
||||
protobufs::TransformationAddDeadContinue message_;
|
||||
};
|
||||
|
@ -66,7 +66,8 @@ bool TransformationAddParameter::IsApplicable(
|
||||
}
|
||||
|
||||
void TransformationAddParameter::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Find the function that will be transformed
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
@ -85,6 +86,11 @@ void TransformationAddParameter::Apply(
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
|
||||
|
||||
// Mark new parameter as irrelevant so that we can replace its use with some
|
||||
// other id.
|
||||
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
|
||||
message_.parameter_fresh_id());
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(), [this](opt::Instruction* call) {
|
||||
@ -93,9 +99,6 @@ void TransformationAddParameter::Apply(
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// it would be good to mark this usage of |id| as irrelevant, so that
|
||||
// we can perform some interesting transformations on it later.
|
||||
call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
});
|
||||
|
||||
|
146
3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp
vendored
Normal file
146
3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_add_relaxed_decoration.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
|
||||
const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration&
|
||||
message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
|
||||
uint32_t result_id) {
|
||||
message_.set_result_id(result_id);
|
||||
}
|
||||
|
||||
bool TransformationAddRelaxedDecoration::IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// |message_.result_id| must be the id of an instruction.
|
||||
auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (!instr) {
|
||||
return false;
|
||||
}
|
||||
opt::BasicBlock* cur_block = ir_context->get_instr_block(instr);
|
||||
// The instruction must have a block.
|
||||
if (cur_block == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// |cur_block| must be a dead block.
|
||||
if (!(transformation_context.GetFactManager()->BlockIsDead(
|
||||
cur_block->id()))) {
|
||||
return false;
|
||||
}
|
||||
// The instruction must be numeric.
|
||||
return IsNumeric(instr->opcode());
|
||||
}
|
||||
|
||||
void TransformationAddRelaxedDecoration::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Add a RelaxedPrecision decoration targeting |message_.result_id|.
|
||||
ir_context->get_decoration_mgr()->AddDecoration(
|
||||
message_.result_id(), SpvDecorationRelaxedPrecision);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_relaxed_decoration() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) {
|
||||
switch (opcode) {
|
||||
case SpvOpConvertFToU:
|
||||
case SpvOpConvertFToS:
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
case SpvOpUConvert:
|
||||
case SpvOpSConvert:
|
||||
case SpvOpFConvert:
|
||||
case SpvOpConvertPtrToU:
|
||||
case SpvOpSatConvertSToU:
|
||||
case SpvOpSatConvertUToS:
|
||||
case SpvOpVectorExtractDynamic:
|
||||
case SpvOpVectorInsertDynamic:
|
||||
case SpvOpVectorShuffle:
|
||||
case SpvOpTranspose:
|
||||
case SpvOpSNegate:
|
||||
case SpvOpFNegate:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpFAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpFSub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpFMul:
|
||||
case SpvOpUDiv:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpFDiv:
|
||||
case SpvOpUMod:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpFRem:
|
||||
case SpvOpFMod:
|
||||
case SpvOpVectorTimesScalar:
|
||||
case SpvOpMatrixTimesScalar:
|
||||
case SpvOpVectorTimesMatrix:
|
||||
case SpvOpMatrixTimesVector:
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
case SpvOpOuterProduct:
|
||||
case SpvOpDot:
|
||||
case SpvOpIAddCarry:
|
||||
case SpvOpISubBorrow:
|
||||
case SpvOpUMulExtended:
|
||||
case SpvOpSMulExtended:
|
||||
case SpvOpShiftRightLogical:
|
||||
case SpvOpShiftRightArithmetic:
|
||||
case SpvOpShiftLeftLogical:
|
||||
case SpvOpBitwiseOr:
|
||||
case SpvOpBitwiseXor:
|
||||
case SpvOpBitwiseAnd:
|
||||
case SpvOpNot:
|
||||
case SpvOpBitFieldInsert:
|
||||
case SpvOpBitFieldSExtract:
|
||||
case SpvOpBitFieldUExtract:
|
||||
case SpvOpBitReverse:
|
||||
case SpvOpBitCount:
|
||||
case SpvOpAtomicLoad:
|
||||
case SpvOpAtomicStore:
|
||||
case SpvOpAtomicExchange:
|
||||
case SpvOpAtomicCompareExchange:
|
||||
case SpvOpAtomicCompareExchangeWeak:
|
||||
case SpvOpAtomicIIncrement:
|
||||
case SpvOpAtomicIDecrement:
|
||||
case SpvOpAtomicIAdd:
|
||||
case SpvOpAtomicISub:
|
||||
case SpvOpAtomicSMin:
|
||||
case SpvOpAtomicUMin:
|
||||
case SpvOpAtomicSMax:
|
||||
case SpvOpAtomicUMax:
|
||||
case SpvOpAtomicAnd:
|
||||
case SpvOpAtomicOr:
|
||||
case SpvOpAtomicXor:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
62
3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h
vendored
Normal file
62
3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
|
||||
#define SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationAddRelaxedDecoration : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddRelaxedDecoration(
|
||||
const protobufs::TransformationAddRelaxedDecoration& message);
|
||||
|
||||
explicit TransformationAddRelaxedDecoration(uint32_t fresh_id);
|
||||
|
||||
// - |message_.result_id| must be the result id of an instruction, which is
|
||||
// located in a dead block and Relaxed decoration can be applied.
|
||||
// - It does not matter whether this instruction is already annotated with the
|
||||
// Relaxed decoration.
|
||||
bool IsApplicable(
|
||||
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds a decoration of the form:
|
||||
// 'OpDecoration |message_.result_id| RelaxedPrecision'
|
||||
// to the module.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if and only if |opcode| is the opcode of an instruction
|
||||
// that operates on 32-bit integers and 32-bit floats
|
||||
// as defined by the SPIR-V specification.
|
||||
static bool IsNumeric(uint32_t opcode);
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddRelaxedDecoration message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
|
@ -38,7 +38,8 @@ TransformationAddSynonym::TransformationAddSynonym(
|
||||
}
|
||||
|
||||
bool TransformationAddSynonym::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
|
||||
message_.synonym_type()) &&
|
||||
"Synonym type is invalid");
|
||||
@ -55,7 +56,8 @@ bool TransformationAddSynonym::IsApplicable(
|
||||
}
|
||||
|
||||
// Check that we can apply |synonym_type| to |result_id|.
|
||||
if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) {
|
||||
if (!IsInstructionValid(ir_context, transformation_context, synonym,
|
||||
message_.synonym_type())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,7 +78,7 @@ bool TransformationAddSynonym::IsApplicable(
|
||||
|
||||
// A constant instruction must be present in the module if required.
|
||||
if (IsAdditionalConstantRequired(message_.synonym_type()) &&
|
||||
MaybeGetConstantId(ir_context) == 0) {
|
||||
MaybeGetConstantId(ir_context, transformation_context) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -90,7 +92,8 @@ void TransformationAddSynonym::Apply(
|
||||
TransformationContext* transformation_context) const {
|
||||
// Add a synonymous instruction.
|
||||
FindInstruction(message_.insert_before(), ir_context)
|
||||
->InsertBefore(MakeSynonymousInstruction(ir_context));
|
||||
->InsertBefore(
|
||||
MakeSynonymousInstruction(ir_context, *transformation_context));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
|
||||
|
||||
@ -122,7 +125,8 @@ protobufs::Transformation TransformationAddSynonym::ToMessage() const {
|
||||
}
|
||||
|
||||
bool TransformationAddSynonym::IsInstructionValid(
|
||||
opt::IRContext* ir_context, opt::Instruction* inst,
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context, opt::Instruction* inst,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type) {
|
||||
// Instruction must have a result id, type id. We skip OpUndef and
|
||||
// OpConstantNull.
|
||||
@ -131,8 +135,10 @@ bool TransformationAddSynonym::IsInstructionValid(
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// We can't create a synonym of an irrelevant id.
|
||||
if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (synonym_type) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
@ -149,7 +155,9 @@ bool TransformationAddSynonym::IsInstructionValid(
|
||||
return type->AsInteger() || type->AsFloat();
|
||||
}
|
||||
case protobufs::TransformationAddSynonym::COPY_OBJECT:
|
||||
return fuzzerutil::CanMakeSynonymOf(ir_context, inst);
|
||||
// All checks for OpCopyObject are handled by
|
||||
// fuzzerutil::CanMakeSynonymOf.
|
||||
return true;
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR: {
|
||||
// The instruction must be either a scalar or a vector of booleans.
|
||||
@ -166,7 +174,8 @@ bool TransformationAddSynonym::IsInstructionValid(
|
||||
|
||||
std::unique_ptr<opt::Instruction>
|
||||
TransformationAddSynonym::MakeSynonymousInstruction(
|
||||
opt::IRContext* ir_context) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
auto synonym_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.result_id());
|
||||
assert(synonym_type_id && "Synonym has invalid type id");
|
||||
@ -206,7 +215,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
|
||||
ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{MaybeGetConstantId(ir_context, transformation_context)}}});
|
||||
}
|
||||
case protobufs::TransformationAddSynonym::COPY_OBJECT:
|
||||
return MakeUnique<opt::Instruction>(
|
||||
@ -224,7 +234,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
|
||||
ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{MaybeGetConstantId(ir_context, transformation_context)}}});
|
||||
}
|
||||
default:
|
||||
assert(false && "Unhandled synonym type");
|
||||
@ -233,7 +244,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
|
||||
}
|
||||
|
||||
uint32_t TransformationAddSynonym::MaybeGetConstantId(
|
||||
opt::IRContext* ir_context) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
|
||||
"Synonym type doesn't require an additional constant");
|
||||
|
||||
@ -245,7 +257,8 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId(
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id);
|
||||
return fuzzerutil::MaybeGetZeroConstant(
|
||||
ir_context, transformation_context, synonym_type_id, false);
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
|
||||
@ -259,19 +272,20 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId(
|
||||
auto one_word =
|
||||
vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
|
||||
if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
|
||||
ir_context, {one_word}, element_type_id)) {
|
||||
ir_context, transformation_context, {one_word}, element_type_id,
|
||||
false)) {
|
||||
return fuzzerutil::MaybeGetCompositeConstant(
|
||||
ir_context,
|
||||
ir_context, transformation_context,
|
||||
std::vector<uint32_t>(vector->element_count(), scalar_one_id),
|
||||
synonym_type_id);
|
||||
synonym_type_id, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return fuzzerutil::MaybeGetScalarConstant(
|
||||
ir_context,
|
||||
ir_context, transformation_context,
|
||||
{synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
|
||||
synonym_type_id);
|
||||
synonym_type_id, false);
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -35,6 +35,7 @@ class TransformationAddSynonym : public Transformation {
|
||||
const protobufs::InstructionDescriptor& insert_before);
|
||||
|
||||
// - |result_id| must be a valid result id of some instruction in the module.
|
||||
// - |result_id| may not be an irrelevant id.
|
||||
// - |synonym_type| is a type of the synonymous instruction that will be
|
||||
// created.
|
||||
// - |synonym_fresh_id| is a fresh id.
|
||||
@ -57,7 +58,9 @@ class TransformationAddSynonym : public Transformation {
|
||||
// Returns true if we can create a synonym of |inst| according to the
|
||||
// |synonym_type|.
|
||||
static bool IsInstructionValid(
|
||||
opt::IRContext* ir_context, opt::Instruction* inst,
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
opt::Instruction* inst,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type);
|
||||
|
||||
// Returns true if |synonym_type| requires an additional constant instruction
|
||||
@ -68,14 +71,17 @@ class TransformationAddSynonym : public Transformation {
|
||||
private:
|
||||
// Returns a new instruction which is synonymous to |message_.result_id|.
|
||||
std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
|
||||
opt::IRContext* ir_context) const;
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const;
|
||||
|
||||
// Returns a result id of a constant instruction that is required to be
|
||||
// present in some synonym types (e.g. returns a result id of a zero constant
|
||||
// for ADD_ZERO synonym type). Returns 0 if no such instruction is present in
|
||||
// the module. This method should only be called when
|
||||
// IsAdditionalConstantRequired returns true.
|
||||
uint32_t MaybeGetConstantId(opt::IRContext* ir_context) const;
|
||||
uint32_t MaybeGetConstantId(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const;
|
||||
|
||||
protobufs::TransformationAddSynonym message_;
|
||||
};
|
||||
|
@ -40,7 +40,8 @@ TransformationCompositeConstruct::TransformationCompositeConstruct(
|
||||
}
|
||||
|
||||
bool TransformationCompositeConstruct::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
// We require the id for the composite constructor to be unused.
|
||||
return false;
|
||||
@ -87,7 +88,20 @@ bool TransformationCompositeConstruct::IsApplicable(
|
||||
|
||||
// Now check whether every component being used to initialize the composite is
|
||||
// available at the desired program point.
|
||||
for (auto& component : message_.component()) {
|
||||
for (auto component : message_.component()) {
|
||||
auto* inst = ir_context->get_def_use_mgr()->GetDef(component);
|
||||
if (!inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should be able to create a synonym of |component| if it's not
|
||||
// irrelevant.
|
||||
if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
|
||||
inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
|
||||
component)) {
|
||||
return false;
|
||||
@ -144,17 +158,23 @@ void TransformationCompositeConstruct::Apply(
|
||||
for (uint32_t subvector_index = 0;
|
||||
subvector_index < component_type->AsVector()->element_count();
|
||||
subvector_index++) {
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(component, {subvector_index}),
|
||||
MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
|
||||
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
component)) {
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(component, {subvector_index}),
|
||||
MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
// The other cases are simple: the component is made directly synonymous
|
||||
// with the element of the composite being constructed.
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(component, {}),
|
||||
MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
|
||||
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
component)) {
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(component, {}),
|
||||
MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ TransformationCompositeExtract::TransformationCompositeExtract(
|
||||
}
|
||||
|
||||
bool TransformationCompositeExtract::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
@ -54,6 +55,14 @@ bool TransformationCompositeExtract::IsApplicable(
|
||||
if (!composite_instruction) {
|
||||
return false;
|
||||
}
|
||||
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.composite_id()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
|
||||
composite_instruction)) {
|
||||
// |composite_id| will participate in DataSynonym facts. Thus, it can't be
|
||||
// an irrelevant id.
|
||||
return false;
|
||||
}
|
||||
if (auto block = ir_context->get_instr_block(composite_instruction)) {
|
||||
if (composite_instruction == instruction_to_insert_before ||
|
||||
!ir_context->GetDominatorAnalysis(block->GetParent())
|
||||
@ -105,17 +114,20 @@ void TransformationCompositeExtract::Apply(
|
||||
|
||||
// Add the fact that the id storing the extracted element is synonymous with
|
||||
// the index into the structure.
|
||||
std::vector<uint32_t> indices;
|
||||
for (auto an_index : message_.index()) {
|
||||
indices.push_back(an_index);
|
||||
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
message_.composite_id())) {
|
||||
std::vector<uint32_t> indices;
|
||||
for (auto an_index : message_.index()) {
|
||||
indices.push_back(an_index);
|
||||
}
|
||||
protobufs::DataDescriptor data_descriptor_for_extracted_element =
|
||||
MakeDataDescriptor(message_.composite_id(), std::move(indices));
|
||||
protobufs::DataDescriptor data_descriptor_for_result_id =
|
||||
MakeDataDescriptor(message_.fresh_id(), {});
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
data_descriptor_for_extracted_element, data_descriptor_for_result_id,
|
||||
ir_context);
|
||||
}
|
||||
protobufs::DataDescriptor data_descriptor_for_extracted_element =
|
||||
MakeDataDescriptor(message_.composite_id(), std::move(indices));
|
||||
protobufs::DataDescriptor data_descriptor_for_result_id =
|
||||
MakeDataDescriptor(message_.fresh_id(), {});
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
data_descriptor_for_extracted_element, data_descriptor_for_result_id,
|
||||
ir_context);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
|
||||
|
@ -48,7 +48,8 @@ class TransformationCompositeExtract : public Transformation {
|
||||
// Adds an OpCompositeConstruct instruction before the instruction identified
|
||||
// by |message_.instruction_to_insert_before|, that extracts from
|
||||
// |message_.composite_id| via indices |message_.index| into
|
||||
// |message_.fresh_id|. Generates a data synonym fact relating
|
||||
// |message_.fresh_id|. If |composite_id| is not an irrelevant id,
|
||||
// generates a data synonym fact relating
|
||||
// |message_.fresh_id| to the extracted element.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
@ -37,7 +37,8 @@ TransformationEquationInstruction::TransformationEquationInstruction(
|
||||
}
|
||||
|
||||
bool TransformationEquationInstruction::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// The result id must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
@ -59,6 +60,9 @@ bool TransformationEquationInstruction::IsApplicable(
|
||||
if (inst->opcode() == SpvOpUndef) {
|
||||
return false;
|
||||
}
|
||||
if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
|
||||
return false;
|
||||
}
|
||||
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
|
||||
id)) {
|
||||
return false;
|
||||
|
@ -87,38 +87,6 @@ void TransformationPermuteFunctionParameters::Apply(
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
auto* old_function_type_inst =
|
||||
fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type_inst && "Function must have a valid type");
|
||||
|
||||
// Change function's type
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) {
|
||||
// If only the current function uses |old_function_type_inst| - change it
|
||||
// in-place.
|
||||
opt::Instruction::OperandList permuted_operands = {
|
||||
std::move(old_function_type_inst->GetInOperand(0))};
|
||||
for (auto index : message_.permutation()) {
|
||||
// +1 since the first operand to OpTypeFunction is a return type.
|
||||
permuted_operands.push_back(
|
||||
std::move(old_function_type_inst->GetInOperand(index + 1)));
|
||||
}
|
||||
|
||||
old_function_type_inst->SetInOperands(std::move(permuted_operands));
|
||||
} else {
|
||||
// Either use an existing type or create a new one.
|
||||
std::vector<uint32_t> type_ids = {
|
||||
old_function_type_inst->GetSingleWordInOperand(0)};
|
||||
for (auto index : message_.permutation()) {
|
||||
// +1 since the first operand to OpTypeFunction is a return type.
|
||||
type_ids.push_back(
|
||||
old_function_type_inst->GetSingleWordInOperand(index + 1));
|
||||
}
|
||||
|
||||
function->DefInst().SetInOperand(
|
||||
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||
ir_context, message_.function_type_fresh_id(), type_ids)});
|
||||
}
|
||||
|
||||
// Adjust OpFunctionParameter instructions
|
||||
|
||||
// Collect ids and types from OpFunctionParameter instructions
|
||||
@ -146,24 +114,40 @@ void TransformationPermuteFunctionParameters::Apply(
|
||||
});
|
||||
|
||||
// Fix all OpFunctionCall instructions
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(), [this](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != message_.function_id()) {
|
||||
return;
|
||||
}
|
||||
for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
opt::Instruction::OperandList call_operands = {
|
||||
call->GetInOperand(0) // Function id
|
||||
};
|
||||
|
||||
opt::Instruction::OperandList call_operands = {
|
||||
call->GetInOperand(0) // Function id
|
||||
};
|
||||
for (auto index : message_.permutation()) {
|
||||
// Take function id into account
|
||||
call_operands.push_back(call->GetInOperand(index + 1));
|
||||
}
|
||||
|
||||
for (auto index : message_.permutation()) {
|
||||
// Take function id into account
|
||||
call_operands.push_back(call->GetInOperand(index + 1));
|
||||
}
|
||||
call->SetInOperands(std::move(call_operands));
|
||||
}
|
||||
|
||||
call->SetInOperands(std::move(call_operands));
|
||||
});
|
||||
// Update function type.
|
||||
{
|
||||
// We use a separate scope here since |old_function_type_inst| might become
|
||||
// a dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
|
||||
|
||||
auto* old_function_type_inst =
|
||||
fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type_inst && "Function must have a valid type");
|
||||
|
||||
std::vector<uint32_t> parameter_type_ids;
|
||||
for (auto index : message_.permutation()) {
|
||||
// +1 since the first operand to OpTypeFunction is a return type.
|
||||
parameter_type_ids.push_back(
|
||||
old_function_type_inst->GetSingleWordInOperand(index + 1));
|
||||
}
|
||||
|
||||
// Change function's type.
|
||||
fuzzerutil::UpdateFunctionType(
|
||||
ir_context, function->result_id(), message_.function_type_fresh_id(),
|
||||
old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids);
|
||||
}
|
||||
|
||||
// Make sure our changes are analyzed
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
|
@ -38,7 +38,8 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
|
||||
}
|
||||
|
||||
bool TransformationPushIdThroughVariable::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// |message_.value_synonym_id| and |message_.variable_id| must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) ||
|
||||
!fuzzerutil::IsFreshId(ir_context, message_.variable_id())) {
|
||||
@ -73,6 +74,14 @@ bool TransformationPushIdThroughVariable::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should be able to create a synonym of |value_id| if it's not irrelevant.
|
||||
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.value_id()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
|
||||
value_instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pointer type instruction pointing to the value type must be defined.
|
||||
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
|
||||
ir_context, value_instruction->type_id(),
|
||||
@ -144,11 +153,14 @@ void TransformationPushIdThroughVariable::Apply(
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
// Adds the fact that |message_.value_synonym_id|
|
||||
// and |message_.value_id| are synonymous.
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.value_synonym_id(), {}),
|
||||
MakeDataDescriptor(message_.value_id(), {}), ir_context);
|
||||
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
message_.value_id())) {
|
||||
// Adds the fact that |message_.value_synonym_id|
|
||||
// and |message_.value_id| are synonymous.
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.value_synonym_id(), {}),
|
||||
MakeDataDescriptor(message_.value_id(), {}), ir_context);
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationPushIdThroughVariable::ToMessage()
|
||||
|
@ -51,8 +51,8 @@ class TransformationPushIdThroughVariable : public Transformation {
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Stores |value_id| to |variable_id|, loads |variable_id| to
|
||||
// |value_synonym_id| and adds the fact that |value_synonym_id| and |value_id|
|
||||
// are synonymous.
|
||||
// |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id|
|
||||
// are synonymous if |value_id| is not irrelevant.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
@ -18,12 +18,6 @@
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
bool IsScalarZeroConstant(const opt::analysis::Constant* constant) {
|
||||
return constant->AsScalarConstant() && constant->IsZero();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TransformationRecordSynonymousConstants::
|
||||
TransformationRecordSynonymousConstants(
|
||||
const protobufs::TransformationRecordSynonymousConstants& message)
|
||||
@ -38,62 +32,30 @@ TransformationRecordSynonymousConstants::
|
||||
|
||||
bool TransformationRecordSynonymousConstants::IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& /* unused */) const {
|
||||
const TransformationContext& transformation_context) const {
|
||||
// The ids must be different
|
||||
if (message_.constant1_id() == message_.constant2_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto constant1 = ir_context->get_constant_mgr()->FindDeclaredConstant(
|
||||
message_.constant1_id());
|
||||
auto constant2 = ir_context->get_constant_mgr()->FindDeclaredConstant(
|
||||
message_.constant2_id());
|
||||
|
||||
// The constants must exist
|
||||
if (constant1 == nullptr || constant2 == nullptr) {
|
||||
if (transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.constant1_id()) ||
|
||||
transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.constant2_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the constants are equal, then they are equivalent
|
||||
if (constant1 == constant2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the constants are two integers (signed or unsigned), they are equal
|
||||
// if they have the same width and the same data words.
|
||||
if (constant1->AsIntConstant() && constant2->AsIntConstant() &&
|
||||
constant1->type()->AsInteger()->width() ==
|
||||
constant2->type()->AsInteger()->width() &&
|
||||
constant1->AsIntConstant()->words() ==
|
||||
constant2->AsIntConstant()->words()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The types must be the same
|
||||
if (!constant1->type()->IsSame(constant2->type())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The constants are equivalent if one is null and the other is a static
|
||||
// constant with value 0.
|
||||
return (constant1->AsNullConstant() && IsScalarZeroConstant(constant2)) ||
|
||||
(IsScalarZeroConstant(constant1) && constant2->AsNullConstant());
|
||||
return AreEquivalentConstants(ir_context, message_.constant1_id(),
|
||||
message_.constant2_id());
|
||||
}
|
||||
|
||||
void TransformationRecordSynonymousConstants::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
protobufs::FactDataSynonym fact_data_synonym;
|
||||
// Define the two equivalent data descriptors (just containing the ids)
|
||||
*fact_data_synonym.mutable_data1() =
|
||||
MakeDataDescriptor(message_.constant1_id(), {});
|
||||
*fact_data_synonym.mutable_data2() =
|
||||
MakeDataDescriptor(message_.constant2_id(), {});
|
||||
protobufs::Fact fact;
|
||||
*fact.mutable_data_synonym_fact() = fact_data_synonym;
|
||||
|
||||
// Add the fact to the fact manager
|
||||
transformation_context->GetFactManager()->AddFact(fact, ir_context);
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.constant1_id(), {}),
|
||||
MakeDataDescriptor(message_.constant2_id(), {}), ir_context);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
|
||||
@ -103,5 +65,62 @@ protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
|
||||
opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) {
|
||||
const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
|
||||
const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
|
||||
|
||||
// Check that the definitions exist
|
||||
if (!def_1 || !def_2) {
|
||||
// We don't use an assertion since otherwise the shrinker fails.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The type ids must be the same
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow
|
||||
// relax this for integers (so that unsigned integer and signed integer are
|
||||
// considered the same type)
|
||||
if (def_1->type_id() != def_2->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
|
||||
auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
|
||||
|
||||
assert(constant1 && constant2 && "The ids must refer to constants.");
|
||||
|
||||
// If either constant is null, the other is equivalent iff it is zero-like
|
||||
if (constant1->AsNullConstant()) {
|
||||
return constant2->IsZero();
|
||||
}
|
||||
|
||||
if (constant2->AsNullConstant()) {
|
||||
return constant1->IsZero();
|
||||
}
|
||||
|
||||
// If the constants are scalar, they are equal iff their words are the same
|
||||
if (auto scalar1 = constant1->AsScalarConstant()) {
|
||||
return scalar1->words() == constant2->AsScalarConstant()->words();
|
||||
}
|
||||
|
||||
// The only remaining possibility is that the constants are composite
|
||||
assert(constant1->AsCompositeConstant() &&
|
||||
"Equivalence of constants can only be checked with scalar, composite "
|
||||
"or null constants.");
|
||||
|
||||
// Since the types match, we already know that the number of components is
|
||||
// the same. We check that the input operands of the definitions are all
|
||||
// constants and that they are pairwise equivalent.
|
||||
for (uint32_t i = 0; i < def_1->NumInOperands(); i++) {
|
||||
if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i),
|
||||
def_2->GetSingleWordInOperand(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, all the components are equivalent
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -33,11 +33,15 @@ class TransformationRecordSynonymousConstants : public Transformation {
|
||||
// of constants
|
||||
// - |message_.constant_id| and |message_.synonym_id| refer to constants
|
||||
// that are equal or equivalent.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Signed and
|
||||
// unsigned integers are currently considered non-equivalent
|
||||
// Two integers with the same width and value are equal, even if one is
|
||||
// signed and the other is not.
|
||||
// Constants are equivalent if both of them represent zero-like scalar
|
||||
// values of the same type (for example OpConstant of type int and value
|
||||
// 0 and OpConstantNull of type int).
|
||||
// Constants are equivalent:
|
||||
// - if both of them represent zero-like values of the same type
|
||||
// - if they are composite constants with the same type and their
|
||||
// components are pairwise equivalent.
|
||||
// - |constant1_id| and |constant2_id| may not be irrelevant.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
@ -51,6 +55,13 @@ class TransformationRecordSynonymousConstants : public Transformation {
|
||||
|
||||
private:
|
||||
protobufs::TransformationRecordSynonymousConstants message_;
|
||||
|
||||
// Returns true if the two given constants are equivalent
|
||||
// (the description of IsApplicable specifies the conditions they must satisfy
|
||||
// to be considered equivalent)
|
||||
static bool AreEquivalentConstants(opt::IRContext* ir_context,
|
||||
uint32_t constant_id1,
|
||||
uint32_t constant_id2);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
127
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
vendored
Normal file
127
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationReplaceCopyMemoryWithLoadStore::
|
||||
TransformationReplaceCopyMemoryWithLoadStore(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationReplaceCopyMemoryWithLoadStore& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationReplaceCopyMemoryWithLoadStore::
|
||||
TransformationReplaceCopyMemoryWithLoadStore(
|
||||
uint32_t fresh_id, const protobufs::InstructionDescriptor&
|
||||
copy_memory_instruction_descriptor) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
*message_.mutable_copy_memory_instruction_descriptor() =
|
||||
copy_memory_instruction_descriptor;
|
||||
}
|
||||
|
||||
bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |message_.fresh_id| must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
// The instruction to be replaced must be defined and have opcode
|
||||
// OpCopyMemory.
|
||||
auto copy_memory_instruction = FindInstruction(
|
||||
message_.copy_memory_instruction_descriptor(), ir_context);
|
||||
if (!copy_memory_instruction ||
|
||||
copy_memory_instruction->opcode() != SpvOpCopyMemory) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationReplaceCopyMemoryWithLoadStore::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto copy_memory_instruction = FindInstruction(
|
||||
message_.copy_memory_instruction_descriptor(), ir_context);
|
||||
// |copy_memory_instruction| must be defined.
|
||||
assert(copy_memory_instruction &&
|
||||
copy_memory_instruction->opcode() == SpvOpCopyMemory &&
|
||||
"The required OpCopyMemory instruction must be defined.");
|
||||
|
||||
// Coherence check: Both operands must be pointers.
|
||||
|
||||
// Get types of ids used as a source and target of |copy_memory_instruction|.
|
||||
auto target = ir_context->get_def_use_mgr()->GetDef(
|
||||
copy_memory_instruction->GetSingleWordInOperand(0));
|
||||
auto source = ir_context->get_def_use_mgr()->GetDef(
|
||||
copy_memory_instruction->GetSingleWordInOperand(1));
|
||||
auto target_type_opcode =
|
||||
ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode();
|
||||
auto source_type_opcode =
|
||||
ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode();
|
||||
|
||||
// Keep release-mode compilers happy. (No unused variables.)
|
||||
(void)target;
|
||||
(void)source;
|
||||
(void)target_type_opcode;
|
||||
(void)source_type_opcode;
|
||||
|
||||
assert(target_type_opcode == SpvOpTypePointer &&
|
||||
source_type_opcode == SpvOpTypePointer &&
|
||||
"Operands must be of type OpTypePointer");
|
||||
|
||||
// Coherence check: |source| and |target| must point to the same type.
|
||||
uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
ir_context, target->type_id());
|
||||
uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
|
||||
ir_context, source->type_id());
|
||||
|
||||
// Keep release-mode compilers happy. (No unused variables.)
|
||||
(void)target_pointee_type;
|
||||
(void)source_pointee_type;
|
||||
|
||||
assert(target_pointee_type == source_pointee_type &&
|
||||
"Operands must have the same type to which they point to.");
|
||||
|
||||
// First, insert the OpStore instruction before the OpCopyMemory instruction
|
||||
// and then insert the OpLoad instruction before the OpStore instruction.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {target->result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
|
||||
|
||||
// Remove the OpCopyMemory instruction.
|
||||
ir_context->KillInst(copy_memory_instruction);
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation
|
||||
TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_replace_copy_memory_with_load_store() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
57
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
vendored
Normal file
57
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
|
||||
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
|
||||
public:
|
||||
explicit TransformationReplaceCopyMemoryWithLoadStore(
|
||||
const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
|
||||
|
||||
TransformationReplaceCopyMemoryWithLoadStore(
|
||||
uint32_t fresh_id, const protobufs::InstructionDescriptor&
|
||||
copy_memory_instruction_descriptor);
|
||||
|
||||
// - |message_.fresh_id| must be fresh.
|
||||
// - |message_.copy_memory_instruction_descriptor| must refer to an
|
||||
// OpCopyMemory instruction.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Replaces instruction OpCopyMemory with loading the source variable to an
|
||||
// intermediate value and storing this value into the target variable of the
|
||||
// original OpCopyMemory instruction.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationReplaceCopyMemoryWithLoadStore message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
|
155
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
vendored
Normal file
155
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationReplaceCopyObjectWithStoreLoad::
|
||||
TransformationReplaceCopyObjectWithStoreLoad(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationReplaceCopyObjectWithStoreLoad& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationReplaceCopyObjectWithStoreLoad::
|
||||
TransformationReplaceCopyObjectWithStoreLoad(
|
||||
uint32_t copy_object_result_id, uint32_t fresh_variable_id,
|
||||
uint32_t variable_storage_class, uint32_t variable_initializer_id) {
|
||||
message_.set_copy_object_result_id(copy_object_result_id);
|
||||
message_.set_fresh_variable_id(fresh_variable_id);
|
||||
message_.set_variable_storage_class(variable_storage_class);
|
||||
message_.set_variable_initializer_id(variable_initializer_id);
|
||||
}
|
||||
|
||||
bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// |message_.fresh_variable_id| must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) {
|
||||
return false;
|
||||
}
|
||||
auto copy_object_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
|
||||
|
||||
// This must be a defined OpCopyObject instruction.
|
||||
if (!copy_object_instruction ||
|
||||
copy_object_instruction->opcode() != SpvOpCopyObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The opcode of the type_id instruction cannot be a OpTypePointer,
|
||||
// because we cannot define a pointer to pointer.
|
||||
if (ir_context->get_def_use_mgr()
|
||||
->GetDef(copy_object_instruction->type_id())
|
||||
->opcode() == SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It must be valid to insert the OpStore and OpLoad instruction before it.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
|
||||
copy_object_instruction) ||
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
|
||||
copy_object_instruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A pointer type instruction pointing to the value type must be defined.
|
||||
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
|
||||
ir_context, copy_object_instruction->type_id(),
|
||||
static_cast<SpvStorageClass>(message_.variable_storage_class()));
|
||||
if (!pointer_type_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that initializer is valid.
|
||||
const auto* constant_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id());
|
||||
if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
|
||||
copy_object_instruction->type_id() != constant_inst->type_id()) {
|
||||
return false;
|
||||
}
|
||||
// |message_.variable_storage_class| must be Private or Function.
|
||||
return message_.variable_storage_class() == SpvStorageClassPrivate ||
|
||||
message_.variable_storage_class() == SpvStorageClassFunction;
|
||||
}
|
||||
|
||||
void TransformationReplaceCopyObjectWithStoreLoad::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
auto copy_object_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
|
||||
// |copy_object_instruction| must be defined.
|
||||
assert(copy_object_instruction &&
|
||||
copy_object_instruction->opcode() == SpvOpCopyObject &&
|
||||
"The required OpCopyObject instruction must be defined.");
|
||||
// Get id used as a source by the OpCopyObject instruction.
|
||||
uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2);
|
||||
// A pointer type instruction pointing to the value type must be defined.
|
||||
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
|
||||
ir_context, copy_object_instruction->type_id(),
|
||||
static_cast<SpvStorageClass>(message_.variable_storage_class()));
|
||||
assert(pointer_type_id && "The required pointer type must be available.");
|
||||
|
||||
// Adds a global or local variable (according to the storage class).
|
||||
if (message_.variable_storage_class() == SpvStorageClassPrivate) {
|
||||
fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
|
||||
pointer_type_id, SpvStorageClassPrivate,
|
||||
message_.variable_initializer_id());
|
||||
} else {
|
||||
auto function_id = ir_context->get_instr_block(copy_object_instruction)
|
||||
->GetParent()
|
||||
->result_id();
|
||||
fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
|
||||
pointer_type_id, function_id,
|
||||
message_.variable_initializer_id());
|
||||
}
|
||||
|
||||
// First, insert the OpLoad instruction before the OpCopyObject instruction
|
||||
// and then insert the OpStore instruction before the OpLoad instruction.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
|
||||
copy_object_instruction
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpLoad, copy_object_instruction->type_id(),
|
||||
message_.copy_object_result_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {src_operand}}})));
|
||||
// Remove the CopyObject instruction.
|
||||
ir_context->KillInst(copy_object_instruction);
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
// Adds the fact that |message_.copy_object_result_id|
|
||||
// and src_operand (id used by OpCopyObject) are synonymous.
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.copy_object_result_id(), {}),
|
||||
MakeDataDescriptor(src_operand, {}), ir_context);
|
||||
}
|
||||
|
||||
protobufs::Transformation
|
||||
TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_replace_copy_object_with_store_load() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
63
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
vendored
Normal file
63
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
|
||||
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationReplaceCopyObjectWithStoreLoad : public Transformation {
|
||||
public:
|
||||
explicit TransformationReplaceCopyObjectWithStoreLoad(
|
||||
const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message);
|
||||
|
||||
TransformationReplaceCopyObjectWithStoreLoad(
|
||||
uint32_t copy_object_result_id, uint32_t fresh_variable_id,
|
||||
uint32_t variable_storage_class, uint32_t variable_initializer_id);
|
||||
|
||||
// - |message_.copy_object_result_id| must be a result id of an OpCopyObject
|
||||
// instruction.
|
||||
// - |message_.fresh_variable_id| must be a fresh id given to variable used by
|
||||
// OpStore.
|
||||
// - |message_.variable_storage_class| must be either StorageClassPrivate or
|
||||
// StorageClassFunction.
|
||||
// - |message_.initializer_id| must be a result id of some constant in the
|
||||
// module. Its type must be equal to the pointee type of the variable that
|
||||
// will be created.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Replaces instruction OpCopyObject with storing into a new variable and
|
||||
// immediately loading from this variable to |result_id| of the original
|
||||
// OpCopyObject instruction.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationReplaceCopyObjectWithStoreLoad message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
|
@ -20,23 +20,6 @@
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
|
||||
uint32_t param_id) {
|
||||
auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
|
||||
assert(param_inst && "Parameter id is invalid");
|
||||
|
||||
for (auto& function : *ir_context->module()) {
|
||||
if (fuzzerutil::InstructionIsFunctionParameter(param_inst, &function)) {
|
||||
return &function;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransformationReplaceParameterWithGlobal::
|
||||
TransformationReplaceParameterWithGlobal(
|
||||
@ -53,7 +36,8 @@ TransformationReplaceParameterWithGlobal::
|
||||
}
|
||||
|
||||
bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// Check that |parameter_id| is valid.
|
||||
const auto* param_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
|
||||
@ -62,8 +46,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
||||
}
|
||||
|
||||
// Check that function exists and is not an entry point.
|
||||
const auto* function =
|
||||
GetFunctionFromParameterId(ir_context, message_.parameter_id());
|
||||
const auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||
ir_context, message_.parameter_id());
|
||||
if (!function ||
|
||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||
return false;
|
||||
@ -81,8 +65,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
||||
}
|
||||
|
||||
// Check that initializer for the global variable exists in the module.
|
||||
if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) ==
|
||||
0) {
|
||||
if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context,
|
||||
param_inst->type_id(), false) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -100,25 +84,31 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
||||
}
|
||||
|
||||
void TransformationReplaceParameterWithGlobal::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
const auto* param_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
|
||||
assert(param_inst && "Parameter must exist");
|
||||
|
||||
// Create global variable to store parameter's value.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// Mark the global variable's pointee as irrelevant if replaced parameter is
|
||||
// irrelevant.
|
||||
fuzzerutil::AddGlobalVariable(
|
||||
ir_context, message_.global_variable_fresh_id(),
|
||||
fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
|
||||
SpvStorageClassPrivate),
|
||||
SpvStorageClassPrivate,
|
||||
fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()));
|
||||
fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context,
|
||||
param_inst->type_id(), false));
|
||||
|
||||
auto* function =
|
||||
GetFunctionFromParameterId(ir_context, message_.parameter_id());
|
||||
// Mark the global variable's pointee as irrelevant if replaced parameter is
|
||||
// irrelevant.
|
||||
if (transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
message_.parameter_id())) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.global_variable_fresh_id());
|
||||
}
|
||||
|
||||
auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||
ir_context, message_.parameter_id());
|
||||
assert(function && "Function must exist");
|
||||
|
||||
// Insert an OpLoad instruction right after OpVariable instructions.
|
||||
@ -152,57 +142,47 @@ void TransformationReplaceParameterWithGlobal::Apply(
|
||||
"Parameter must exist in the function");
|
||||
|
||||
// Update all relevant OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
function->result_id(),
|
||||
[ir_context, parameter_index, this](opt::Instruction* inst) {
|
||||
if (inst->opcode() != SpvOpFunctionCall) {
|
||||
return;
|
||||
}
|
||||
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
|
||||
"Can't insert OpStore right before the function call");
|
||||
|
||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
|
||||
"Can't insert OpStore right before the function call");
|
||||
// Insert an OpStore before the OpFunctionCall. +1 since the first
|
||||
// operand of OpFunctionCall is an id of the function.
|
||||
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{inst->GetSingleWordInOperand(parameter_index + 1)}}}));
|
||||
|
||||
// Insert an OpStore before the OpFunctionCall. +1 since the first
|
||||
// operand of OpFunctionCall is an id of the function.
|
||||
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{inst->GetSingleWordInOperand(parameter_index + 1)}}}));
|
||||
|
||||
// +1 since the first operand of OpFunctionCall is an id of the
|
||||
// function.
|
||||
inst->RemoveInOperand(parameter_index + 1);
|
||||
});
|
||||
// +1 since the first operand of OpFunctionCall is an id of the
|
||||
// function.
|
||||
inst->RemoveInOperand(parameter_index + 1);
|
||||
}
|
||||
|
||||
// Remove the parameter from the function.
|
||||
function->RemoveParameter(message_.parameter_id());
|
||||
|
||||
// Update function's type.
|
||||
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function has invalid type");
|
||||
{
|
||||
// We use a separate scope here since |old_function_type| might become a
|
||||
// dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
|
||||
|
||||
// Preemptively add function's return type id.
|
||||
std::vector<uint32_t> type_ids = {
|
||||
old_function_type->GetSingleWordInOperand(0)};
|
||||
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function has invalid type");
|
||||
|
||||
// +1 and -1 since the first operand is the return type id.
|
||||
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
|
||||
if (i - 1 != parameter_index) {
|
||||
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
|
||||
// +1 and -1 since the first operand is the return type id.
|
||||
std::vector<uint32_t> parameter_type_ids;
|
||||
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
|
||||
if (i - 1 != parameter_index) {
|
||||
parameter_type_ids.push_back(
|
||||
old_function_type->GetSingleWordInOperand(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
|
||||
// Change the old type in place. +1 since the first operand is the result
|
||||
// type id of the function.
|
||||
old_function_type->RemoveInOperand(parameter_index + 1);
|
||||
} else {
|
||||
// Find an existing or create a new function type.
|
||||
function->DefInst().SetInOperand(
|
||||
1, {fuzzerutil::FindOrCreateFunctionType(
|
||||
ir_context, message_.function_type_fresh_id(), type_ids)});
|
||||
fuzzerutil::UpdateFunctionType(
|
||||
ir_context, function->result_id(), message_.function_type_fresh_id(),
|
||||
old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
|
||||
}
|
||||
|
||||
// Make sure our changes are analyzed
|
||||
|
306
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp
vendored
Normal file
306
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||
const protobufs::TransformationReplaceParamsWithStruct& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||
const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
|
||||
uint32_t fresh_parameter_id,
|
||||
const std::unordered_map<uint32_t, uint32_t>&
|
||||
caller_id_to_fresh_composite_id) {
|
||||
message_.set_fresh_function_type_id(fresh_function_type_id);
|
||||
message_.set_fresh_parameter_id(fresh_parameter_id);
|
||||
|
||||
for (auto id : parameter_id) {
|
||||
message_.add_parameter_id(id);
|
||||
}
|
||||
|
||||
message_.mutable_caller_id_to_fresh_composite_id()->insert(
|
||||
caller_id_to_fresh_composite_id.begin(),
|
||||
caller_id_to_fresh_composite_id.end());
|
||||
}
|
||||
|
||||
bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
std::vector<uint32_t> parameter_id(message_.parameter_id().begin(),
|
||||
message_.parameter_id().end());
|
||||
|
||||
// Check that |parameter_id| is neither empty nor it has duplicates.
|
||||
if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All ids must correspond to valid parameters of the same function.
|
||||
// The function can't be an entry-point function.
|
||||
|
||||
// fuzzerutil::GetFunctionFromParameterId requires a valid id.
|
||||
if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* function =
|
||||
fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]);
|
||||
if (!function ||
|
||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute all ids of the function's parameters.
|
||||
std::unordered_set<uint32_t> all_parameter_ids;
|
||||
for (const auto* param :
|
||||
fuzzerutil::GetParameters(ir_context, function->result_id())) {
|
||||
all_parameter_ids.insert(param->result_id());
|
||||
}
|
||||
|
||||
// Check that all elements in |parameter_id| are valid.
|
||||
for (auto id : parameter_id) {
|
||||
// fuzzerutil::GetFunctionFromParameterId requires a valid id.
|
||||
if (!ir_context->get_def_use_mgr()->GetDef(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |id| is a result id of one of the |function|'s parameters.
|
||||
if (!all_parameter_ids.count(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the parameter with result id |id| has supported type.
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(
|
||||
fuzzerutil::GetTypeId(ir_context, id));
|
||||
assert(type && "Parameter has invalid type");
|
||||
if (!IsParameterTypeSupported(*type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We already know that the function has at least |parameter_id.size()|
|
||||
// parameters.
|
||||
|
||||
// Check that a relevant OpTypeStruct exists in the module.
|
||||
if (!MaybeGetRequiredStructType(ir_context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |callee_id_to_fresh_composite_id| is valid.
|
||||
for (const auto* inst :
|
||||
fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
// Check that the callee is present in the map. It's ok if the map contains
|
||||
// more ids that there are callees (those ids will not be used).
|
||||
if (!message_.caller_id_to_fresh_composite_id().contains(
|
||||
inst->result_id())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all fresh ids are unique and fresh.
|
||||
std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
|
||||
message_.fresh_parameter_id()};
|
||||
|
||||
for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
|
||||
fresh_ids.push_back(entry.second);
|
||||
}
|
||||
|
||||
return !fuzzerutil::HasDuplicates(fresh_ids) &&
|
||||
std::all_of(fresh_ids.begin(), fresh_ids.end(),
|
||||
[ir_context](uint32_t id) {
|
||||
return fuzzerutil::IsFreshId(ir_context, id);
|
||||
});
|
||||
}
|
||||
|
||||
void TransformationReplaceParamsWithStruct::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto* function = fuzzerutil::GetFunctionFromParameterId(
|
||||
ir_context, message_.parameter_id(0));
|
||||
assert(function &&
|
||||
"All parameters' ids should've been checked in the IsApplicable");
|
||||
|
||||
// Get a type id of the OpTypeStruct used as a type id of the new parameter.
|
||||
auto struct_type_id = MaybeGetRequiredStructType(ir_context);
|
||||
assert(struct_type_id &&
|
||||
"IsApplicable should've guaranteed that this value isn't equal to 0");
|
||||
|
||||
// Add new parameter to the function.
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFunctionParameter, struct_type_id,
|
||||
message_.fresh_parameter_id(), opt::Instruction::OperandList()));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
|
||||
|
||||
// Compute indices of replaced parameters. This will be used to adjust
|
||||
// OpFunctionCall instructions and create OpCompositeConstruct instructions at
|
||||
// every call site.
|
||||
std::vector<uint32_t> indices_of_replaced_params;
|
||||
{
|
||||
// We want to destroy |params| after the loop because it will contain
|
||||
// dangling pointers when we remove parameters from the function.
|
||||
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
|
||||
for (auto id : message_.parameter_id()) {
|
||||
auto it = std::find_if(params.begin(), params.end(),
|
||||
[id](const opt::Instruction* param) {
|
||||
return param->result_id() == id;
|
||||
});
|
||||
assert(it != params.end() && "Parameter's id is invalid");
|
||||
indices_of_replaced_params.push_back(
|
||||
static_cast<uint32_t>(it - params.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
// Update all function calls.
|
||||
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
// Create a list of operands for the OpCompositeConstruct instruction.
|
||||
opt::Instruction::OperandList composite_components;
|
||||
for (auto index : indices_of_replaced_params) {
|
||||
// +1 since the first in operand to OpFunctionCall is the result id of
|
||||
// the function.
|
||||
composite_components.emplace_back(
|
||||
std::move(inst->GetInOperand(index + 1)));
|
||||
}
|
||||
|
||||
// Remove arguments from the function call. We do it in a separate loop
|
||||
// and in reverse order to make sure we have removed correct operands.
|
||||
for (auto it = indices_of_replaced_params.rbegin();
|
||||
it != indices_of_replaced_params.rend(); ++it) {
|
||||
// +1 since the first in operand to OpFunctionCall is the result id of
|
||||
// the function.
|
||||
inst->RemoveInOperand(*it + 1);
|
||||
}
|
||||
|
||||
// Insert OpCompositeConstruct before the function call.
|
||||
auto fresh_composite_id =
|
||||
message_.caller_id_to_fresh_composite_id().at(inst->result_id());
|
||||
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
|
||||
std::move(composite_components)));
|
||||
|
||||
// Add a new operand to the OpFunctionCall instruction.
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id);
|
||||
}
|
||||
|
||||
// Insert OpCompositeExtract instructions into the entry point block of the
|
||||
// function and remove replaced parameters.
|
||||
for (int i = 0; i < message_.parameter_id_size(); ++i) {
|
||||
const auto* param_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i));
|
||||
assert(param_inst && "Parameter id is invalid");
|
||||
|
||||
// Skip all OpVariable instructions.
|
||||
auto iter = function->begin()->begin();
|
||||
while (iter != function->begin()->end() &&
|
||||
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
|
||||
iter)) {
|
||||
++iter;
|
||||
}
|
||||
|
||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
|
||||
iter) &&
|
||||
"Can't extract parameter's value from the structure");
|
||||
|
||||
// Insert OpCompositeExtract instructions to unpack parameters' values from
|
||||
// the struct type.
|
||||
iter.InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract, param_inst->type_id(),
|
||||
param_inst->result_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
|
||||
|
||||
function->RemoveParameter(param_inst->result_id());
|
||||
}
|
||||
|
||||
// Update function's type.
|
||||
{
|
||||
// We use a separate scope here since |old_function_type| might become a
|
||||
// dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
|
||||
|
||||
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(old_function_type && "Function has invalid type");
|
||||
|
||||
// +1 since the first in operand to OpTypeFunction is the result type id
|
||||
// of the function.
|
||||
std::vector<uint32_t> parameter_type_ids;
|
||||
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
|
||||
if (std::find(indices_of_replaced_params.begin(),
|
||||
indices_of_replaced_params.end(),
|
||||
i - 1) == indices_of_replaced_params.end()) {
|
||||
parameter_type_ids.push_back(
|
||||
old_function_type->GetSingleWordInOperand(i));
|
||||
}
|
||||
}
|
||||
|
||||
parameter_type_ids.push_back(struct_type_id);
|
||||
|
||||
fuzzerutil::UpdateFunctionType(
|
||||
ir_context, function->result_id(), message_.fresh_function_type_id(),
|
||||
old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
|
||||
}
|
||||
|
||||
// Make sure our changes are analyzed
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_replace_params_with_struct() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported(
|
||||
const opt::analysis::Type& param_type) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Consider adding support for more types of parameters.
|
||||
switch (param_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::kVector:
|
||||
case opt::analysis::Type::kMatrix:
|
||||
return true;
|
||||
case opt::analysis::Type::kStruct:
|
||||
return std::all_of(param_type.AsStruct()->element_types().begin(),
|
||||
param_type.AsStruct()->element_types().end(),
|
||||
[](const opt::analysis::Type* type) {
|
||||
return IsParameterTypeSupported(*type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
|
||||
opt::IRContext* ir_context) const {
|
||||
std::vector<uint32_t> component_type_ids;
|
||||
for (auto id : message_.parameter_id()) {
|
||||
component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id));
|
||||
}
|
||||
|
||||
return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
83
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h
vendored
Normal file
83
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationReplaceParamsWithStruct : public Transformation {
|
||||
public:
|
||||
explicit TransformationReplaceParamsWithStruct(
|
||||
const protobufs::TransformationReplaceParamsWithStruct& message);
|
||||
|
||||
TransformationReplaceParamsWithStruct(
|
||||
const std::vector<uint32_t>& parameter_id,
|
||||
uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
|
||||
const std::unordered_map<uint32_t, uint32_t>&
|
||||
caller_id_to_fresh_composite_id);
|
||||
|
||||
// - Each element of |parameter_id| is a valid result id of some
|
||||
// OpFunctionParameter instruction. All parameter ids must correspond to
|
||||
// parameters of the same function. That function may not be an entry-point
|
||||
// function.
|
||||
// - Types of all parameters must be supported by this transformation (see
|
||||
// IsParameterTypeSupported method).
|
||||
// - |parameter_id| may not be empty or contain duplicates.
|
||||
// - There must exist an OpTypeStruct instruction containing types of all
|
||||
// replaced parameters. Type of the i'th component of the struct is equal
|
||||
// to the type of the instruction with result id |parameter_id[i]|.
|
||||
// - |caller_id_to_fresh_composite_id| should contain a key for at least every
|
||||
// result id of an OpFunctionCall instruction that calls the function.
|
||||
// - |fresh_function_type_id|, |fresh_parameter_id|,
|
||||
// |caller_id_to_fresh_composite_id| are all fresh and unique ids.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Creates a new function parameter with result id |fresh_parameter_id|.
|
||||
// Parameter's type is OpTypeStruct with each components type equal to the
|
||||
// type of the replaced parameter.
|
||||
// - OpCompositeConstruct with result id from |fresh_composite_id| is inserted
|
||||
// before each OpFunctionCall instruction.
|
||||
// - OpCompositeExtract with result id equal to the result id of the replaced
|
||||
// parameter is created in the function.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if parameter's type is supported by this transformation.
|
||||
static bool IsParameterTypeSupported(const opt::analysis::Type& param_type);
|
||||
|
||||
private:
|
||||
// Returns a result id of the OpTypeStruct instruction required by this
|
||||
// transformation (see docs on the IsApplicable method to learn more).
|
||||
uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationReplaceParamsWithStruct message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
@ -39,7 +39,8 @@ TransformationVectorShuffle::TransformationVectorShuffle(
|
||||
}
|
||||
|
||||
bool TransformationVectorShuffle::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// The fresh id must not already be in use.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
@ -56,12 +57,26 @@ bool TransformationVectorShuffle::IsApplicable(
|
||||
if (!vector1_instruction || !vector1_instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
// We should be able to create a synonym of |vector1| if it's not irrelevant.
|
||||
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.vector1()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
|
||||
vector1_instruction)) {
|
||||
return false;
|
||||
}
|
||||
// The second vector must be an instruction with a type id
|
||||
auto vector2_instruction =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.vector2());
|
||||
if (!vector2_instruction || !vector2_instruction->type_id()) {
|
||||
return false;
|
||||
}
|
||||
// We should be able to create a synonym of |vector2| if it's not irrelevant.
|
||||
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
|
||||
message_.vector2()) &&
|
||||
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
|
||||
vector2_instruction)) {
|
||||
return false;
|
||||
}
|
||||
auto vector1_type =
|
||||
ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
|
||||
// The first vector instruction's type must actually be a vector type.
|
||||
@ -161,9 +176,21 @@ void TransformationVectorShuffle::Apply(
|
||||
// |component| refers.
|
||||
if (component <
|
||||
GetVectorType(ir_context, message_.vector1())->element_count()) {
|
||||
// Irrelevant id cannot participate in DataSynonym facts.
|
||||
if (transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
message_.vector1())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
descriptor_for_source_component =
|
||||
MakeDataDescriptor(message_.vector1(), {component});
|
||||
} else {
|
||||
// Irrelevant id cannot participate in DataSynonym facts.
|
||||
if (transformation_context->GetFactManager()->IdIsIrrelevant(
|
||||
message_.vector2())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto index_into_vector_2 =
|
||||
component -
|
||||
GetVectorType(ir_context, message_.vector1())->element_count();
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
#include "source/opt/types.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -59,7 +58,9 @@ class TransformationVectorShuffle : public Transformation {
|
||||
// from which it came (with undefined components being ignored). If the
|
||||
// result vector is a contiguous sub-range of one of the input vectors, a
|
||||
// fact is added to record that |message_.fresh_id| is synonymous with this
|
||||
// sub-range.
|
||||
// sub-range. DataSynonym facts are added only for non-irrelevant vectors
|
||||
// (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be
|
||||
// created for |vector1| but not |vector2|).
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
|
4
3rdparty/spirv-tools/source/opcode.cpp
vendored
4
3rdparty/spirv-tools/source/opcode.cpp
vendored
@ -413,6 +413,7 @@ bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
|
||||
case SpvOpAtomicIIncrement:
|
||||
case SpvOpAtomicIDecrement:
|
||||
case SpvOpAtomicIAdd:
|
||||
case SpvOpAtomicFAddEXT:
|
||||
case SpvOpAtomicISub:
|
||||
case SpvOpAtomicSMin:
|
||||
case SpvOpAtomicUMin:
|
||||
@ -445,7 +446,7 @@ bool spvOpcodeIsReturn(SpvOp opcode) {
|
||||
|
||||
bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
|
||||
return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
|
||||
opcode == SpvOpUnreachable;
|
||||
opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation;
|
||||
}
|
||||
|
||||
bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
|
||||
@ -700,6 +701,7 @@ std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
|
||||
case SpvOpAtomicIIncrement:
|
||||
case SpvOpAtomicIDecrement:
|
||||
case SpvOpAtomicIAdd:
|
||||
case SpvOpAtomicFAddEXT:
|
||||
case SpvOpAtomicISub:
|
||||
case SpvOpAtomicSMin:
|
||||
case SpvOpAtomicUMin:
|
||||
|
@ -38,6 +38,8 @@ const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
|
||||
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
|
||||
const uint32_t kCopyMemoryTargetAddrInIdx = 0;
|
||||
const uint32_t kCopyMemorySourceAddrInIdx = 1;
|
||||
const uint32_t kDebugDeclareOperandVariableIndex = 5;
|
||||
const uint32_t kGlobalVariableVariableIndex = 12;
|
||||
|
||||
// Sorting functor to present annotation instructions in an easy-to-process
|
||||
// order. The functor orders by opcode first and falls back on unique id
|
||||
@ -470,6 +472,19 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
if (varId != 0) {
|
||||
ProcessLoad(func, varId);
|
||||
}
|
||||
// If DebugDeclare, process as load of variable
|
||||
} else if (liveInst->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugDeclare) {
|
||||
uint32_t varId =
|
||||
liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
ProcessLoad(func, varId);
|
||||
// If DebugValue with Deref, process as load of variable
|
||||
} else if (liveInst->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugValue) {
|
||||
uint32_t varId = context()
|
||||
->get_debug_info_mgr()
|
||||
->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
|
||||
if (varId != 0) ProcessLoad(func, varId);
|
||||
// If merge, add other branches that are part of its control structure
|
||||
} else if (liveInst->opcode() == SpvOpLoopMerge ||
|
||||
liveInst->opcode() == SpvOpSelectionMerge) {
|
||||
@ -621,6 +636,18 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each DebugInfo GlobalVariable keep all operands except the Variable.
|
||||
// Later, if the variable is dead, we will set the operand to DebugInfoNone.
|
||||
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
|
||||
if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable)
|
||||
continue;
|
||||
dbg.ForEachInId([this](const uint32_t* iid) {
|
||||
Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (inInst->opcode() == SpvOpVariable) return;
|
||||
AddToWorklist(inInst);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
@ -840,6 +867,26 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& dbg : get_module()->ext_inst_debuginfo()) {
|
||||
if (!IsDead(&dbg)) continue;
|
||||
// Save GlobalVariable if its variable is live, otherwise null out variable
|
||||
// index
|
||||
if (dbg.GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugGlobalVariable) {
|
||||
auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
|
||||
if (!IsDead(var_inst)) continue;
|
||||
context()->ForgetUses(&dbg);
|
||||
dbg.SetOperand(
|
||||
kGlobalVariableVariableIndex,
|
||||
{context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
|
||||
context()->AnalyzeUses(&dbg);
|
||||
continue;
|
||||
}
|
||||
to_kill_.push_back(&dbg);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Since ADCE is disabled for non-shaders, we don't check for export linkage
|
||||
// attributes here.
|
||||
for (auto& val : get_module()->types_values()) {
|
||||
@ -939,6 +986,7 @@ void AggressiveDCEPass::InitExtensions() {
|
||||
"SPV_KHR_ray_tracing",
|
||||
"SPV_EXT_fragment_invocation_density",
|
||||
"SPV_EXT_physical_storage_buffer",
|
||||
"SPV_KHR_terminate_invocation",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -214,6 +214,7 @@ bool CodeSinkingPass::HasUniformMemorySync() {
|
||||
case SpvOpAtomicIIncrement:
|
||||
case SpvOpAtomicIDecrement:
|
||||
case SpvOpAtomicIAdd:
|
||||
case SpvOpAtomicFAddEXT:
|
||||
case SpvOpAtomicISub:
|
||||
case SpvOpAtomicSMin:
|
||||
case SpvOpAtomicUMin:
|
||||
|
@ -148,6 +148,11 @@ class DebugInfoManager {
|
||||
// Erases |instr| from data structures of this class.
|
||||
void ClearDebugInfo(Instruction* instr);
|
||||
|
||||
// Returns the id of Value operand if |inst| is DebugValue who has Deref
|
||||
// operation and its Value operand is a result id of OpVariable with
|
||||
// Function storage class. Otherwise, returns 0.
|
||||
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
||||
|
||||
private:
|
||||
IRContext* context() { return context_; }
|
||||
|
||||
@ -178,11 +183,6 @@ class DebugInfoManager {
|
||||
// Returns a DebugExpression instruction without Operation operands.
|
||||
Instruction* GetEmptyDebugExpression();
|
||||
|
||||
// Returns the id of Value operand if |inst| is DebugValue who has Deref
|
||||
// operation and its Value operand is a result id of OpVariable with
|
||||
// Function storage class. Otherwise, returns 0.
|
||||
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
||||
|
||||
// Returns true if a scope |ancestor| is |scope| or an ancestor scope
|
||||
// of |scope|.
|
||||
bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
|
||||
|
@ -176,7 +176,8 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
|
||||
// The tree construction requires 1 entry point, so we add a dummy node
|
||||
// that is connected to all function exiting basic blocks.
|
||||
// An exiting basic block is a block with an OpKill, OpUnreachable,
|
||||
// OpReturn or OpReturnValue as terminator instruction.
|
||||
// OpReturn, OpReturnValue, or OpTerminateInvocation as terminator
|
||||
// instruction.
|
||||
for (BasicBlock& bb : f) {
|
||||
if (bb.hasSuccessor()) {
|
||||
BasicBlockListTy& pred_list = predecessors_[&bb];
|
||||
|
13
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
13
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
@ -384,7 +384,8 @@ std::unique_ptr<BasicBlock> InlinePass::InlineReturn(
|
||||
for (auto callee_block_itr = calleeFn->begin();
|
||||
callee_block_itr != calleeFn->end(); ++callee_block_itr) {
|
||||
if (callee_block_itr->tail()->opcode() == SpvOpUnreachable ||
|
||||
callee_block_itr->tail()->opcode() == SpvOpKill) {
|
||||
callee_block_itr->tail()->opcode() == SpvOpKill ||
|
||||
callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) {
|
||||
returnLabelId = context()->TakeNextId();
|
||||
break;
|
||||
}
|
||||
@ -738,16 +739,18 @@ bool InlinePass::IsInlinableFunction(Function* func) {
|
||||
bool func_is_called_from_continue =
|
||||
funcs_called_from_continue_.count(func->result_id()) != 0;
|
||||
|
||||
if (func_is_called_from_continue && ContainsKill(func)) {
|
||||
if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InlinePass::ContainsKill(Function* func) const {
|
||||
return !func->WhileEachInst(
|
||||
[](Instruction* inst) { return inst->opcode() != SpvOpKill; });
|
||||
bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const {
|
||||
return !func->WhileEachInst([](Instruction* inst) {
|
||||
const auto opcode = inst->opcode();
|
||||
return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation);
|
||||
});
|
||||
}
|
||||
|
||||
void InlinePass::InitializeInline() {
|
||||
|
@ -139,8 +139,9 @@ class InlinePass : public Pass {
|
||||
// Return true if |func| is a function that can be inlined.
|
||||
bool IsInlinableFunction(Function* func);
|
||||
|
||||
// Returns true if |func| contains an OpKill instruction.
|
||||
bool ContainsKill(Function* func) const;
|
||||
// Returns true if |func| contains an OpKill or OpTerminateInvocation
|
||||
// instruction.
|
||||
bool ContainsKillOrTerminateInvocation(Function* func) const;
|
||||
|
||||
// Update phis in succeeding blocks to point to new last block
|
||||
void UpdateSucceedingPhis(
|
||||
|
@ -382,6 +382,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
|
||||
"SPV_KHR_ray_tracing",
|
||||
"SPV_KHR_ray_query",
|
||||
"SPV_EXT_fragment_invocation_density",
|
||||
"SPV_KHR_terminate_invocation",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -267,6 +267,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
|
||||
"SPV_KHR_ray_query",
|
||||
"SPV_EXT_fragment_invocation_density",
|
||||
"SPV_EXT_physical_storage_buffer",
|
||||
"SPV_KHR_terminate_invocation",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
|
||||
"SPV_KHR_ray_query",
|
||||
"SPV_EXT_fragment_invocation_density",
|
||||
"SPV_EXT_physical_storage_buffer",
|
||||
"SPV_KHR_terminate_invocation",
|
||||
});
|
||||
}
|
||||
bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
|
||||
|
@ -997,7 +997,8 @@ bool LoopUtils::CanPerformUnroll() {
|
||||
const BasicBlock* block = context_->cfg()->block(label_id);
|
||||
if (block->ctail()->opcode() == SpvOp::SpvOpKill ||
|
||||
block->ctail()->opcode() == SpvOp::SpvOpReturn ||
|
||||
block->ctail()->opcode() == SpvOp::SpvOpReturnValue) {
|
||||
block->ctail()->opcode() == SpvOp::SpvOpReturnValue ||
|
||||
block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -299,9 +299,6 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
|
||||
// There is at least one values that needs to be replaced.
|
||||
// First create the OpPhi instruction.
|
||||
InstructionBuilder builder(
|
||||
context(), &*merge_block->begin(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
uint32_t undef_id = Type2Undef(inst.type_id());
|
||||
std::vector<uint32_t> phi_operands;
|
||||
const std::set<uint32_t>& new_edges = new_edges_[merge_block];
|
||||
@ -318,7 +315,50 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
phi_operands.push_back(pred_id);
|
||||
}
|
||||
|
||||
Instruction* new_phi = builder.AddPhi(inst.type_id(), phi_operands);
|
||||
Instruction* new_phi = nullptr;
|
||||
// If the instruction is a pointer and variable pointers are not an option,
|
||||
// then we have to regenerate the instruction instead of creating an OpPhi
|
||||
// instruction. If not, the Spir-V will be invalid.
|
||||
Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id());
|
||||
bool regenerateInstruction = false;
|
||||
if (inst_type->opcode() == SpvOpTypePointer) {
|
||||
if (!context()->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityVariablePointers)) {
|
||||
regenerateInstruction = true;
|
||||
}
|
||||
|
||||
uint32_t storage_class = inst_type->GetSingleWordInOperand(0);
|
||||
if (storage_class != SpvStorageClassWorkgroup &&
|
||||
storage_class != SpvStorageClassStorageBuffer) {
|
||||
regenerateInstruction = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (regenerateInstruction) {
|
||||
std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
|
||||
uint32_t new_id = TakeNextId();
|
||||
regen_inst->SetResultId(new_id);
|
||||
Instruction* insert_pos = &*merge_block->begin();
|
||||
while (insert_pos->opcode() == SpvOpPhi) {
|
||||
insert_pos = insert_pos->NextNode();
|
||||
}
|
||||
new_phi = insert_pos->InsertBefore(std::move(regen_inst));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
|
||||
context()->set_instr_block(new_phi, merge_block);
|
||||
|
||||
new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
|
||||
Instruction* use = get_def_use_mgr()->GetDef(*use_id);
|
||||
BasicBlock* use_bb = context()->get_instr_block(use);
|
||||
if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
|
||||
CreatePhiNodesForInst(merge_block, *use);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
InstructionBuilder builder(
|
||||
context(), &*merge_block->begin(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
new_phi = builder.AddPhi(inst.type_id(), phi_operands);
|
||||
}
|
||||
uint32_t result_of_phi = new_phi->result_id();
|
||||
|
||||
// Update all of the users to use the result of the new OpPhi.
|
||||
|
3
3rdparty/spirv-tools/source/opt/reflect.h
vendored
3
3rdparty/spirv-tools/source/opt/reflect.h
vendored
@ -60,7 +60,8 @@ inline bool IsSpecConstantInst(SpvOp opcode) {
|
||||
return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp;
|
||||
}
|
||||
inline bool IsTerminatorInst(SpvOp opcode) {
|
||||
return opcode >= SpvOpBranch && opcode <= SpvOpUnreachable;
|
||||
return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) ||
|
||||
(opcode == SpvOpTerminateInvocation);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
|
@ -141,6 +141,7 @@ bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction(
|
||||
// TODO: Teach |ReplaceInstruction| to handle block terminators. Then
|
||||
// uncomment the OpKill case.
|
||||
// case SpvOpKill:
|
||||
// case SpvOpTerminateInstruction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
53
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
53
3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
vendored
@ -27,7 +27,8 @@ Pass::Status WrapOpKill::Process() {
|
||||
for (uint32_t func_id : func_to_process) {
|
||||
Function* func = context()->GetFunction(func_id);
|
||||
bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpKill) {
|
||||
const auto opcode = inst->opcode();
|
||||
if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) {
|
||||
modified = true;
|
||||
if (!ReplaceWithFunctionCall(inst)) {
|
||||
return false;
|
||||
@ -46,16 +47,22 @@ Pass::Status WrapOpKill::Process() {
|
||||
"The function should only be generated if something was modified.");
|
||||
context()->AddFunction(std::move(opkill_function_));
|
||||
}
|
||||
if (opterminateinvocation_function_ != nullptr) {
|
||||
assert(modified &&
|
||||
"The function should only be generated if something was modified.");
|
||||
context()->AddFunction(std::move(opterminateinvocation_function_));
|
||||
}
|
||||
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
|
||||
}
|
||||
|
||||
bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
|
||||
assert(inst->opcode() == SpvOpKill &&
|
||||
"|inst| must be an OpKill instruction.");
|
||||
assert((inst->opcode() == SpvOpKill ||
|
||||
inst->opcode() == SpvOpTerminateInvocation) &&
|
||||
"|inst| must be an OpKill or OpTerminateInvocation instruction.");
|
||||
InstructionBuilder ir_builder(
|
||||
context(), inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
uint32_t func_id = GetOpKillFuncId();
|
||||
uint32_t func_id = GetKillingFuncId(inst->opcode());
|
||||
if (func_id == 0) {
|
||||
return false;
|
||||
}
|
||||
@ -108,13 +115,20 @@ uint32_t WrapOpKill::GetVoidFunctionTypeId() {
|
||||
return type_mgr->GetTypeInstruction(&func_type);
|
||||
}
|
||||
|
||||
uint32_t WrapOpKill::GetOpKillFuncId() {
|
||||
if (opkill_function_ != nullptr) {
|
||||
return opkill_function_->result_id();
|
||||
uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) {
|
||||
// Parameterize by opcode
|
||||
assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation);
|
||||
|
||||
std::unique_ptr<Function>* const killing_func =
|
||||
(opcode == SpvOpKill) ? &opkill_function_
|
||||
: &opterminateinvocation_function_;
|
||||
|
||||
if (*killing_func != nullptr) {
|
||||
return (*killing_func)->result_id();
|
||||
}
|
||||
|
||||
uint32_t opkill_func_id = TakeNextId();
|
||||
if (opkill_func_id == 0) {
|
||||
uint32_t killing_func_id = TakeNextId();
|
||||
if (killing_func_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -125,15 +139,15 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
|
||||
|
||||
// Generate the function start instruction
|
||||
std::unique_ptr<Instruction> func_start(new Instruction(
|
||||
context(), SpvOpFunction, void_type_id, opkill_func_id, {}));
|
||||
context(), SpvOpFunction, void_type_id, killing_func_id, {}));
|
||||
func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
|
||||
func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
|
||||
opkill_function_.reset(new Function(std::move(func_start)));
|
||||
(*killing_func).reset(new Function(std::move(func_start)));
|
||||
|
||||
// Generate the function end instruction
|
||||
std::unique_ptr<Instruction> func_end(
|
||||
new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
|
||||
opkill_function_->SetFunctionEnd(std::move(func_end));
|
||||
(*killing_func)->SetFunctionEnd(std::move(func_end));
|
||||
|
||||
// Create the one basic block for the function.
|
||||
uint32_t lab_id = TakeNextId();
|
||||
@ -146,21 +160,22 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
|
||||
|
||||
// Add the OpKill to the basic block
|
||||
std::unique_ptr<Instruction> kill_inst(
|
||||
new Instruction(context(), SpvOpKill, 0, 0, {}));
|
||||
new Instruction(context(), opcode, 0, 0, {}));
|
||||
bb->AddInstruction(std::move(kill_inst));
|
||||
|
||||
// Add the bb to the function
|
||||
bb->SetParent(opkill_function_.get());
|
||||
opkill_function_->AddBasicBlock(std::move(bb));
|
||||
bb->SetParent((*killing_func).get());
|
||||
(*killing_func)->AddBasicBlock(std::move(bb));
|
||||
|
||||
// Add the function to the module.
|
||||
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
|
||||
opkill_function_->ForEachInst(
|
||||
[this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
|
||||
(*killing_func)->ForEachInst([this](Instruction* inst) {
|
||||
context()->AnalyzeDefUse(inst);
|
||||
});
|
||||
}
|
||||
|
||||
if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
|
||||
for (BasicBlock& basic_block : *opkill_function_) {
|
||||
for (BasicBlock& basic_block : *(*killing_func)) {
|
||||
context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
|
||||
for (Instruction& inst : basic_block) {
|
||||
context()->set_instr_block(&inst, &basic_block);
|
||||
@ -168,7 +183,7 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
|
||||
}
|
||||
}
|
||||
|
||||
return opkill_function_->result_id();
|
||||
return (*killing_func)->result_id();
|
||||
}
|
||||
|
||||
uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user