Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2020-07-26 17:02:28 -07:00
parent 381d381c93
commit fd71b4060d
105 changed files with 3057 additions and 857 deletions

View File

@ -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

View File

@ -1 +1 @@
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898"
"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"

View File

@ -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}
};

View File

@ -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 "";

View File

@ -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,

View File

@ -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[] = {

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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_;

View File

@ -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:

View File

@ -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() {

View File

@ -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)));
});
}

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}

View File

@ -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));

View File

@ -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));
}
}
}

View File

@ -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()));
}
}

View 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

View 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

View File

@ -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:

View File

@ -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),

View File

@ -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),

View File

@ -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);
}

View File

@ -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.

View File

@ -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(

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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

View File

@ -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(

View 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

View 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

View 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

View 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

View File

@ -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(),

View 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, &params](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(&parameter_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

View 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_

View File

@ -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,

View File

@ -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|.

View File

@ -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

View File

@ -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());

View File

@ -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 {

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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_;
};

View File

@ -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

View File

@ -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_;
};

View File

@ -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()}});
});

View 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

View 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

View File

@ -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:

View File

@ -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_;
};

View File

@ -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++;
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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(

View File

@ -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()

View File

@ -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;

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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

View File

@ -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

View 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

View 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_

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -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",
});
}

View File

@ -214,6 +214,7 @@ bool CodeSinkingPass::HasUniformMemorySync() {
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicIAdd:
case SpvOpAtomicFAddEXT:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:

View File

@ -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);

View File

@ -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];

View File

@ -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() {

View File

@ -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(

View File

@ -382,6 +382,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
"SPV_KHR_ray_tracing",
"SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
"SPV_KHR_terminate_invocation",
});
}

View File

@ -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",
});
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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