Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2019-01-25 22:09:33 -08:00
parent eae8dd2d23
commit 1675484726
62 changed files with 1563 additions and 601 deletions

View File

@@ -76,6 +76,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/aggressive_dead_code_elim_pass.cpp \
source/opt/basic_block.cpp \
source/opt/block_merge_pass.cpp \
source/opt/block_merge_util.cpp \
source/opt/build_module.cpp \
source/opt/cfg.cpp \
source/opt/cfg_cleanup_pass.cpp \

View File

@@ -128,6 +128,23 @@ See the [CHANGES](CHANGES) file for reports on completed work, and the [General
sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for
planned and in-progress work.
### Reducer
*Note:* The reducer is still under development.
The reducer simplifies and shrinks a SPIR-V module with respect to a
user-supplied *interestingness function*. For example, given a large
SPIR-V module that cause some SPIR-V compiler to fail with a given
fatal error message, the reducer could be used to look for a smaller
version of the module that causes the compiler to fail with the same
fatal error message.
To suggest an additional capability for the reducer, [file an
issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with
"Reducer:" as the start of its title.
### Extras
* [Utility filters](#utility-filters)

View File

@@ -1 +1 @@
"v2019.2-dev", "SPIRV-Tools v2019.2-dev 7d149d91055744e5a9bc6368de7b3bb605d0e1a9"
"v2019.2-dev", "SPIRV-Tools v2019.2-dev eae8dd2d23578d8b3cbb404d2caa01b27d69da15"

View File

@@ -1,5 +1,6 @@
static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddresses};
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer};
static const SpvCapability pygen_variable_caps_AddressesPhysicalStorageBufferAddressesEXT[] = {SpvCapabilityAddresses, SpvCapabilityPhysicalStorageBufferAddressesEXT};
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddressesEXT[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer, SpvCapabilityPhysicalStorageBufferAddressesEXT};
static const SpvCapability pygen_variable_caps_DerivativeControl[] = {SpvCapabilityDerivativeControl};
static const SpvCapability pygen_variable_caps_DeviceEnqueue[] = {SpvCapabilityDeviceEnqueue};
static const SpvCapability pygen_variable_caps_FragmentMaskAMD[] = {SpvCapabilityFragmentMaskAMD};
@@ -82,7 +83,7 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
{"TypeReserveId", SpvOpTypeReserveId, 1, pygen_variable_caps_Pipes, 1, {SPV_OPERAND_TYPE_RESULT_ID}, 1, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"TypeQueue", SpvOpTypeQueue, 1, pygen_variable_caps_DeviceEnqueue, 1, {SPV_OPERAND_TYPE_RESULT_ID}, 1, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"TypePipe", SpvOpTypePipe, 1, pygen_variable_caps_Pipes, 2, {SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ACCESS_QUALIFIER}, 1, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"TypeForwardPointer", SpvOpTypeForwardPointer, 1, pygen_variable_caps_Addresses, 2, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_STORAGE_CLASS}, 0, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"TypeForwardPointer", SpvOpTypeForwardPointer, 2, pygen_variable_caps_AddressesPhysicalStorageBufferAddressesEXT, 2, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_STORAGE_CLASS}, 0, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConstantTrue", SpvOpConstantTrue, 0, nullptr, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConstantFalse", SpvOpConstantFalse, 0, nullptr, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"Constant", SpvOpConstant, 0, nullptr, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
@@ -106,7 +107,7 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
{"CopyMemorySized", SpvOpCopyMemorySized, 1, pygen_variable_caps_Addresses, 4, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS}, 0, 0, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"AccessChain", SpvOpAccessChain, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"InBoundsAccessChain", SpvOpInBoundsAccessChain, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"PtrAccessChain", SpvOpPtrAccessChain, 3, pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"PtrAccessChain", SpvOpPtrAccessChain, 4, pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddressesEXT, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ArrayLength", SpvOpArrayLength, 1, pygen_variable_caps_Shader, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"GenericPtrMemSemantics", SpvOpGenericPtrMemSemantics, 1, pygen_variable_caps_Kernel, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"InBoundsPtrAccessChain", SpvOpInBoundsPtrAccessChain, 1, pygen_variable_caps_Addresses, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
@@ -153,10 +154,10 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
{"SConvert", SpvOpSConvert, 0, nullptr, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"FConvert", SpvOpFConvert, 0, nullptr, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"QuantizeToF16", SpvOpQuantizeToF16, 0, nullptr, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConvertPtrToU", SpvOpConvertPtrToU, 1, pygen_variable_caps_Addresses, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConvertPtrToU", SpvOpConvertPtrToU, 2, pygen_variable_caps_AddressesPhysicalStorageBufferAddressesEXT, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"SatConvertSToU", SpvOpSatConvertSToU, 1, pygen_variable_caps_Kernel, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"SatConvertUToS", SpvOpSatConvertUToS, 1, pygen_variable_caps_Kernel, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConvertUToPtr", SpvOpConvertUToPtr, 1, pygen_variable_caps_Addresses, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ConvertUToPtr", SpvOpConvertUToPtr, 2, pygen_variable_caps_AddressesPhysicalStorageBufferAddressesEXT, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"PtrCastToGeneric", SpvOpPtrCastToGeneric, 1, pygen_variable_caps_Kernel, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"GenericCastToPtr", SpvOpGenericCastToPtr, 1, pygen_variable_caps_Kernel, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},
{"GenericCastToPtrExplicit", SpvOpGenericCastToPtrExplicit, 1, pygen_variable_caps_Kernel, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_STORAGE_CLASS}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1, 0)},

View File

@@ -26,6 +26,8 @@ const char* ExtensionToString(Extension extension) {
return "SPV_EXT_fragment_fully_covered";
case Extension::kSPV_EXT_fragment_invocation_density:
return "SPV_EXT_fragment_invocation_density";
case Extension::kSPV_EXT_physical_storage_buffer:
return "SPV_EXT_physical_storage_buffer";
case Extension::kSPV_EXT_shader_stencil_export:
return "SPV_EXT_shader_stencil_export";
case Extension::kSPV_EXT_shader_viewport_index_layer:
@@ -97,8 +99,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_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_INTEL_subgroups", "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_post_depth_coverage", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "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_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_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_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, 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_INTEL_subgroups, 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_post_depth_coverage, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, 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_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_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_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "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_INTEL_subgroups", "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_post_depth_coverage", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "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_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_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_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, 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_INTEL_subgroups, 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_post_depth_coverage, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, 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_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_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(
@@ -364,6 +366,8 @@ const char* CapabilityToString(SpvCapability capability) {
return "ComputeDerivativeGroupLinearNV";
case SpvCapabilityFragmentDensityEXT:
return "FragmentDensityEXT";
case SpvCapabilityPhysicalStorageBufferAddressesEXT:
return "PhysicalStorageBufferAddressesEXT";
case SpvCapabilityMax:
assert(0 && "Attempting to convert SpvCapabilityMax to string");
return "";

View File

@@ -11,6 +11,7 @@ kSPV_AMD_texture_gather_bias_lod,
kSPV_EXT_descriptor_indexing,
kSPV_EXT_fragment_fully_covered,
kSPV_EXT_fragment_invocation_density,
kSPV_EXT_physical_storage_buffer,
kSPV_EXT_shader_stencil_export,
kSPV_EXT_shader_viewport_index_layer,
kSPV_GOOGLE_decorate_string,

View File

@@ -42,6 +42,7 @@ static const SpvCapability pygen_variable_caps_MinLod[] = {SpvCapabilityMinLod};
static const SpvCapability pygen_variable_caps_MultiView[] = {SpvCapabilityMultiView};
static const SpvCapability pygen_variable_caps_MultiViewport[] = {SpvCapabilityMultiViewport};
static const SpvCapability pygen_variable_caps_PerViewAttributesNVMeshShadingNV[] = {SpvCapabilityPerViewAttributesNV, SpvCapabilityMeshShadingNV};
static const SpvCapability pygen_variable_caps_PhysicalStorageBufferAddressesEXT[] = {SpvCapabilityPhysicalStorageBufferAddressesEXT};
static const SpvCapability pygen_variable_caps_Pipes[] = {SpvCapabilityPipes};
static const SpvCapability pygen_variable_caps_RayTracingNV[] = {SpvCapabilityRayTracingNV};
static const SpvCapability pygen_variable_caps_RoundingModeRTE[] = {SpvCapabilityRoundingModeRTE};
@@ -87,6 +88,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_AMD_texture_gather_bias
static const spvtools::Extension pygen_variable_exts_SPV_EXT_descriptor_indexing[] = {spvtools::Extension::kSPV_EXT_descriptor_indexing};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_fully_covered[] = {spvtools::Extension::kSPV_EXT_fragment_fully_covered};
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_physical_storage_buffer[] = {spvtools::Extension::kSPV_EXT_physical_storage_buffer};
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_layer[] = {spvtools::Extension::kSPV_EXT_shader_viewport_index_layer};
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
@@ -231,7 +233,8 @@ static const spv_operand_desc_t pygen_variable_ExecutionModelEntries[] = {
static const spv_operand_desc_t pygen_variable_AddressingModelEntries[] = {
{"Logical", 0, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"Physical32", 1, 1, pygen_variable_caps_Addresses, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"Physical64", 2, 1, pygen_variable_caps_Addresses, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0)}
{"Physical64", 2, 1, pygen_variable_caps_Addresses, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"PhysicalStorageBuffer64EXT", 5348, 1, pygen_variable_caps_PhysicalStorageBufferAddressesEXT, 1, pygen_variable_exts_SPV_EXT_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1, 0)}
};
static const spv_operand_desc_t pygen_variable_MemoryModelEntries[] = {
@@ -313,7 +316,8 @@ static const spv_operand_desc_t pygen_variable_StorageClassEntries[] = {
{"RayPayloadNV", 5338, 1, pygen_variable_caps_RayTracingNV, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"HitAttributeNV", 5339, 1, pygen_variable_caps_RayTracingNV, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"IncomingRayPayloadNV", 5342, 1, pygen_variable_caps_RayTracingNV, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"ShaderRecordBufferNV", 5343, 1, pygen_variable_caps_RayTracingNV, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, SPV_SPIRV_VERSION_WORD(1, 0)}
{"ShaderRecordBufferNV", 5343, 1, pygen_variable_caps_RayTracingNV, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"PhysicalStorageBufferEXT", 5349, 1, pygen_variable_caps_PhysicalStorageBufferAddressesEXT, 1, pygen_variable_exts_SPV_EXT_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1, 0)}
};
static const spv_operand_desc_t pygen_variable_DimEntries[] = {
@@ -513,6 +517,8 @@ static const spv_operand_desc_t pygen_variable_DecorationEntries[] = {
{"PerTaskNV", 5273, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {}, 0xffffffffu},
{"PerVertexNV", 5285, 1, pygen_variable_caps_FragmentBarycentricNV, 1, pygen_variable_exts_SPV_NV_fragment_shader_barycentric, {}, 0xffffffffu},
{"NonUniformEXT", 5300, 1, pygen_variable_caps_ShaderNonUniformEXT, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1, 0)},
{"RestrictPointerEXT", 5355, 1, pygen_variable_caps_PhysicalStorageBufferAddressesEXT, 1, pygen_variable_exts_SPV_EXT_physical_storage_buffer, {}, 0xffffffffu},
{"AliasedPointerEXT", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddressesEXT, 1, pygen_variable_exts_SPV_EXT_physical_storage_buffer, {}, 0xffffffffu},
{"HlslCounterBufferGOOGLE", 5634, 0, nullptr, 1, pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1, {SPV_OPERAND_TYPE_ID}, 0xffffffffu},
{"HlslSemanticGOOGLE", 5635, 0, nullptr, 1, pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1, {SPV_OPERAND_TYPE_LITERAL_STRING}, 0xffffffffu}
};
@@ -769,6 +775,7 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
{"RayTracingNV", 5340, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_NV_ray_tracing, {}, 0xffffffffu},
{"VulkanMemoryModelKHR", 5345, 0, nullptr, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, 0xffffffffu},
{"VulkanMemoryModelDeviceScopeKHR", 5346, 0, nullptr, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, 0xffffffffu},
{"PhysicalStorageBufferAddressesEXT", 5347, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_physical_storage_buffer, {}, 0xffffffffu},
{"ComputeDerivativeGroupLinearNV", 5350, 0, nullptr, 1, pygen_variable_exts_SPV_NV_compute_shader_derivatives, {}, 0xffffffffu},
{"SubgroupShuffleINTEL", 5568, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu},
{"SubgroupBufferBlockIOINTEL", 5569, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu},

View File

@@ -235,6 +235,9 @@ class SpirvTools {
bool Validate(const uint32_t* binary, size_t binary_size,
spv_validator_options options) const;
// Was this object successfully constructed.
bool IsValid() const;
private:
struct Impl; // Opaque struct for holding the data fields used by this class.
std::unique_ptr<Impl> impl_; // Unique pointer to implementation data.

View File

@@ -128,4 +128,6 @@ bool SpirvTools::Validate(const uint32_t* binary, const size_t binary_size,
return valid;
}
bool SpirvTools::IsValid() const { return impl_->context != nullptr; }
} // namespace spvtools

View File

@@ -15,6 +15,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
aggressive_dead_code_elim_pass.h
basic_block.h
block_merge_pass.h
block_merge_util.h
build_module.h
ccp_pass.h
cfg_cleanup_pass.h
@@ -108,6 +109,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
aggressive_dead_code_elim_pass.cpp
basic_block.cpp
block_merge_pass.cpp
block_merge_util.cpp
build_module.cpp
ccp_pass.cpp
cfg_cleanup_pass.cpp

View File

@@ -18,6 +18,7 @@
#include <vector>
#include "source/opt/block_merge_util.h"
#include "source/opt/ir_context.h"
#include "source/opt/iterator.h"
@@ -27,112 +28,17 @@ namespace opt {
bool BlockMergePass::MergeBlocks(Function* func) {
bool modified = false;
for (auto bi = func->begin(); bi != func->end();) {
// Find block with single successor which has no other predecessors.
auto ii = bi->end();
--ii;
Instruction* br = &*ii;
if (br->opcode() != SpvOpBranch) {
if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) {
blockmergeutil::MergeWithSuccessor(context(), func, bi);
// Reprocess block.
modified = true;
} else {
++bi;
continue;
}
const uint32_t lab_id = br->GetSingleWordInOperand(0);
if (cfg()->preds(lab_id).size() != 1) {
++bi;
continue;
}
bool pred_is_merge = IsMerge(&*bi);
bool succ_is_merge = IsMerge(lab_id);
if (pred_is_merge && succ_is_merge) {
// Cannot merge two merges together.
++bi;
continue;
}
Instruction* merge_inst = bi->GetMergeInst();
bool pred_is_header = IsHeader(&*bi);
if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) {
bool succ_is_header = IsHeader(lab_id);
if (pred_is_header && succ_is_header) {
// Cannot merge two headers together when the successor is not the merge
// block of the predecessor.
++bi;
continue;
}
// If this is a header block and the successor is not its merge, we must
// be careful about which blocks we are willing to merge together.
// OpLoopMerge must be followed by a conditional or unconditional branch.
// The merge must be a loop merge because a selection merge cannot be
// followed by an unconditional branch.
BasicBlock* succ_block = context()->get_instr_block(lab_id);
SpvOp succ_term_op = succ_block->terminator()->opcode();
assert(merge_inst->opcode() == SpvOpLoopMerge);
if (succ_term_op != SpvOpBranch &&
succ_term_op != SpvOpBranchConditional) {
++bi;
continue;
}
}
// Merge blocks.
context()->KillInst(br);
auto sbi = bi;
for (; sbi != func->end(); ++sbi)
if (sbi->id() == lab_id) break;
// If bi is sbi's only predecessor, it dominates sbi and thus
// sbi must follow bi in func's ordering.
assert(sbi != func->end());
// Update the inst-to-block mapping for the instructions in sbi.
for (auto& inst : *sbi) {
context()->set_instr_block(&inst, &*bi);
}
// Now actually move the instructions.
bi->AddInstructions(&*sbi);
if (merge_inst) {
if (pred_is_header && lab_id == merge_inst->GetSingleWordInOperand(0u)) {
// Merging the header and merge blocks, so remove the structured control
// flow declaration.
context()->KillInst(merge_inst);
} else {
// Move the merge instruction to just before the terminator.
merge_inst->InsertBefore(bi->terminator());
}
}
context()->ReplaceAllUsesWith(lab_id, bi->id());
context()->KillInst(sbi->GetLabelInst());
(void)sbi.Erase();
// Reprocess block.
modified = true;
}
return modified;
}
bool BlockMergePass::IsHeader(BasicBlock* block) {
return block->GetMergeInst() != nullptr;
}
bool BlockMergePass::IsHeader(uint32_t id) {
return IsHeader(context()->get_instr_block(get_def_use_mgr()->GetDef(id)));
}
bool BlockMergePass::IsMerge(uint32_t id) {
return !get_def_use_mgr()->WhileEachUse(id, [](Instruction* user,
uint32_t index) {
SpvOp op = user->opcode();
if ((op == SpvOpLoopMerge || op == SpvOpSelectionMerge) && index == 0u) {
return false;
}
return true;
});
}
bool BlockMergePass::IsMerge(BasicBlock* block) { return IsMerge(block->id()); }
Pass::Status BlockMergePass::Process() {
// Process all entry point functions.
ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); };

View File

@@ -54,14 +54,6 @@ class BlockMergePass : public Pass {
// with no other predecessors. Merge these blocks into a single block.
bool MergeBlocks(Function* func);
// Returns true if |block| (or |id|) contains a merge instruction.
bool IsHeader(BasicBlock* block);
bool IsHeader(uint32_t id);
// Returns true if |block| (or |id|) is the merge target of a merge
// instruction.
bool IsMerge(BasicBlock* block);
bool IsMerge(uint32_t id);
};
} // namespace opt

View File

@@ -0,0 +1,147 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "block_merge_util.h"
namespace spvtools {
namespace opt {
namespace blockmergeutil {
namespace {
// Returns true if |block| contains a merge instruction.
bool IsHeader(BasicBlock* block) { return block->GetMergeInst() != nullptr; }
// Returns true if |id| contains a merge instruction.
bool IsHeader(IRContext* context, uint32_t id) {
return IsHeader(
context->get_instr_block(context->get_def_use_mgr()->GetDef(id)));
}
// Returns true if |id| is the merge target of a merge instruction.
bool IsMerge(IRContext* context, uint32_t id) {
return !context->get_def_use_mgr()->WhileEachUse(id, [](Instruction* user,
uint32_t index) {
SpvOp op = user->opcode();
if ((op == SpvOpLoopMerge || op == SpvOpSelectionMerge) && index == 0u) {
return false;
}
return true;
});
}
// Returns true if |block| is the merge target of a merge instruction.
bool IsMerge(IRContext* context, BasicBlock* block) {
return IsMerge(context, block->id());
}
} // Anonymous namespace
bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) {
// Find block with single successor which has no other predecessors.
auto ii = block->end();
--ii;
Instruction* br = &*ii;
if (br->opcode() != SpvOpBranch) {
return false;
}
const uint32_t lab_id = br->GetSingleWordInOperand(0);
if (context->cfg()->preds(lab_id).size() != 1) {
return false;
}
bool pred_is_merge = IsMerge(context, block);
bool succ_is_merge = IsMerge(context, lab_id);
if (pred_is_merge && succ_is_merge) {
// Cannot merge two merges together.
return false;
}
Instruction* merge_inst = block->GetMergeInst();
const bool pred_is_header = IsHeader(block);
if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) {
bool succ_is_header = IsHeader(context, lab_id);
if (pred_is_header && succ_is_header) {
// Cannot merge two headers together when the successor is not the merge
// block of the predecessor.
return false;
}
// If this is a header block and the successor is not its merge, we must
// be careful about which blocks we are willing to merge together.
// OpLoopMerge must be followed by a conditional or unconditional branch.
// The merge must be a loop merge because a selection merge cannot be
// followed by an unconditional branch.
BasicBlock* succ_block = context->get_instr_block(lab_id);
SpvOp succ_term_op = succ_block->terminator()->opcode();
assert(merge_inst->opcode() == SpvOpLoopMerge);
if (succ_term_op != SpvOpBranch && succ_term_op != SpvOpBranchConditional) {
return false;
}
}
return true;
}
void MergeWithSuccessor(IRContext* context, Function* func,
Function::iterator bi) {
assert(CanMergeWithSuccessor(context, &*bi) &&
"Precondition failure for MergeWithSuccessor: it must be legal to "
"merge the block and its successor.");
auto ii = bi->end();
--ii;
Instruction* br = &*ii;
const uint32_t lab_id = br->GetSingleWordInOperand(0);
Instruction* merge_inst = bi->GetMergeInst();
bool pred_is_header = IsHeader(&*bi);
// Merge blocks.
context->KillInst(br);
auto sbi = bi;
for (; sbi != func->end(); ++sbi)
if (sbi->id() == lab_id) break;
// If bi is sbi's only predecessor, it dominates sbi and thus
// sbi must follow bi in func's ordering.
assert(sbi != func->end());
// Update the inst-to-block mapping for the instructions in sbi.
for (auto& inst : *sbi) {
context->set_instr_block(&inst, &*bi);
}
// Now actually move the instructions.
bi->AddInstructions(&*sbi);
if (merge_inst) {
if (pred_is_header && lab_id == merge_inst->GetSingleWordInOperand(0u)) {
// Merging the header and merge blocks, so remove the structured control
// flow declaration.
context->KillInst(merge_inst);
} else {
// Move the merge instruction to just before the terminator.
merge_inst->InsertBefore(bi->terminator());
}
}
context->ReplaceAllUsesWith(lab_id, bi->id());
context->KillInst(sbi->GetLabelInst());
(void)sbi.Erase();
}
} // namespace blockmergeutil
} // namespace opt
} // namespace spvtools

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_OPT_BLOCK_MERGE_UTIL_H_
#define SOURCE_OPT_BLOCK_MERGE_UTIL_H_
#include "source/opt/ir_context.h"
namespace spvtools {
namespace opt {
// Provides functions for determining when it is safe to merge blocks, and for
// actually merging blocks, for use by various analyses and passes.
namespace blockmergeutil {
// Returns true if and only if |block| has exactly one successor and merging
// this successor into |block| has no impact on the semantics or validity of the
// SPIR-V module.
bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block);
// Requires that |bi| has a successor that can be safely merged into |bi|, and
// performs the merge.
void MergeWithSuccessor(IRContext* context, Function* func,
Function::iterator bi);
} // namespace blockmergeutil
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_BLOCK_MERGE_UTIL_H_

View File

@@ -80,7 +80,10 @@ BasicBlock* CodeSinkingPass::FindNewBasicBlockFor(Instruction* inst) {
get_def_use_mgr()->ForEachUse(
inst, [&bbs_with_uses, this](Instruction* use, uint32_t idx) {
if (use->opcode() != SpvOpPhi) {
bbs_with_uses.insert(context()->get_instr_block(use)->id());
BasicBlock* use_bb = context()->get_instr_block(use);
if (use_bb) {
bbs_with_uses.insert(use_bb->id());
}
} else {
bbs_with_uses.insert(use->GetSingleWordOperand(idx + 1));
}

View File

@@ -89,6 +89,11 @@ void IRContext::InvalidateAnalysesExceptFor(
}
void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
// The ConstantManager contains Type pointers. If the TypeManager goes
// away, the ConstantManager has to go away.
if (analyses_to_invalidate & kAnalysisTypes) {
analyses_to_invalidate |= kAnalysisConstants;
}
if (analyses_to_invalidate & kAnalysisDefUse) {
def_use_mgr_.reset(nullptr);
}
@@ -127,9 +132,6 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
constant_mgr_.reset(nullptr);
}
if (analyses_to_invalidate & kAnalysisTypes) {
// The ConstantManager contains Type pointers. If the TypeManager goes
// away, the ConstantManager has to go away.
constant_mgr_.reset(nullptr);
type_mgr_.reset(nullptr);
}

View File

@@ -14,33 +14,34 @@
set(SPIRV_TOOLS_REDUCE_SOURCES
change_operand_reduction_opportunity.h
change_operand_to_undef_reduction_opportunity.h
operand_to_const_reduction_pass.h
operand_to_undef_reduction_pass.h
operand_to_dominating_id_reduction_pass.h
operand_to_const_reduction_opportunity_finder.h
operand_to_undef_reduction_opportunity_finder.h
operand_to_dominating_id_reduction_opportunity_finder.h
reducer.h
reduction_opportunity.h
reduction_opportunity_finder.h
reduction_pass.h
reduction_util.h
remove_instruction_reduction_opportunity.h
remove_opname_instruction_reduction_pass.h
remove_unreferenced_instruction_reduction_pass.h
remove_opname_instruction_reduction_opportunity_finder.h
remove_unreferenced_instruction_reduction_opportunity_finder.h
structured_loop_to_selection_reduction_opportunity.h
structured_loop_to_selection_reduction_pass.h
structured_loop_to_selection_reduction_opportunity_finder.h
change_operand_reduction_opportunity.cpp
change_operand_to_undef_reduction_opportunity.cpp
operand_to_const_reduction_pass.cpp
operand_to_undef_reduction_pass.cpp
operand_to_dominating_id_reduction_pass.cpp
operand_to_const_reduction_opportunity_finder.cpp
operand_to_undef_reduction_opportunity_finder.cpp
operand_to_dominating_id_reduction_opportunity_finder.cpp
reducer.cpp
reduction_opportunity.cpp
reduction_pass.cpp
reduction_util.cpp
remove_instruction_reduction_opportunity.cpp
remove_unreferenced_instruction_reduction_pass.cpp
remove_opname_instruction_reduction_pass.cpp
remove_unreferenced_instruction_reduction_opportunity_finder.cpp
remove_opname_instruction_reduction_opportunity_finder.cpp
structured_loop_to_selection_reduction_opportunity.cpp
structured_loop_to_selection_reduction_pass.cpp
structured_loop_to_selection_reduction_opportunity_finder.cpp
)
if(MSVC)

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/operand_to_const_reduction_pass.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
#include "source/opt/instruction.h"
#include "source/reduce/change_operand_reduction_opportunity.h"
@@ -23,7 +23,7 @@ namespace reduce {
using namespace opt;
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToConstReductionPass::GetAvailableOpportunities(
OperandToConstReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
assert(result.empty());
@@ -75,8 +75,8 @@ OperandToConstReductionPass::GetAvailableOpportunities(
return result;
}
std::string OperandToConstReductionPass::GetName() const {
return "OperandToConstReductionPass";
std::string OperandToConstReductionOpportunityFinder::GetName() const {
return "OperandToConstReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,30 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
#define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_
#include "source/reduce/reduction_pass.h"
#include "source/reduce/reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A reduction pass for replacing id operands of instructions with ids of
// A finder for opportunities to replace id operands of instructions with ids of
// constants. This reduces the extent to which ids of non-constants are used,
// paving the way for instructions that generate them to be eliminated by other
// passes.
class OperandToConstReductionPass : public ReductionPass {
// paving the way for instructions that generate them to be eliminated.
class OperandToConstReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit OperandToConstReductionPass(const spv_target_env target_env)
: ReductionPass(target_env) {}
OperandToConstReductionOpportunityFinder() = default;
~OperandToConstReductionPass() override = default;
~OperandToConstReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -45,4 +41,4 @@ class OperandToConstReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "operand_to_dominating_id_reduction_pass.h"
#include "operand_to_dominating_id_reduction_opportunity_finder.h"
#include "change_operand_reduction_opportunity.h"
#include "source/opt/instruction.h"
@@ -22,7 +22,7 @@ namespace reduce {
using namespace opt;
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToDominatingIdReductionPass::GetAvailableOpportunities(
OperandToDominatingIdReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
@@ -55,11 +55,12 @@ OperandToDominatingIdReductionPass::GetAvailableOpportunities(
return result;
}
void OperandToDominatingIdReductionPass::GetOpportunitiesForDominatingInst(
std::vector<std::unique_ptr<ReductionOpportunity>>* opportunities,
opt::Instruction* candidate_dominator,
opt::Function::iterator candidate_dominator_block, opt::Function* function,
opt::IRContext* context) const {
void OperandToDominatingIdReductionOpportunityFinder::
GetOpportunitiesForDominatingInst(
std::vector<std::unique_ptr<ReductionOpportunity>>* opportunities,
opt::Instruction* candidate_dominator,
opt::Function::iterator candidate_dominator_block,
opt::Function* function, opt::IRContext* context) const {
assert(candidate_dominator->HasResultId());
assert(candidate_dominator->type_id());
auto dominator_analysis = context->GetDominatorAnalysis(function);
@@ -106,8 +107,8 @@ void OperandToDominatingIdReductionPass::GetOpportunitiesForDominatingInst(
}
}
std::string OperandToDominatingIdReductionPass::GetName() const {
return "OperandToDominatingIdReductionPass";
std::string OperandToDominatingIdReductionOpportunityFinder::GetName() const {
return "OperandToDominatingIdReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,36 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
#define SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_
#include "reduction_pass.h"
#include "reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A reduction pass that aims to bring to SPIR-V (and generalize) the idea from
// human-readable languages of e.g. replacing an expression with one of its
// arguments, (x + y) -> x, or with a reference to an identifier that was
// assigned to higher up in the program. The generalization of this is to
// replace an id with a different id of the same type defined in some
// dominating instruction.
// A finder that aims to bring to SPIR-V (and generalize) the idea from
// human-readable languages of e.g. finding opportunities to replace an
// expression with one of its arguments, (x + y) -> x, or with a reference to an
// identifier that was assigned to higher up in the program. The generalization
// of this is to replace an id with a different id of the same type defined in
// some dominating instruction.
//
// If id x is defined and then used several times, changing each use of x to
// some dominating definition may eventually allow the statement defining x
// to be eliminated by another pass.
class OperandToDominatingIdReductionPass : public ReductionPass {
class OperandToDominatingIdReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit OperandToDominatingIdReductionPass(const spv_target_env target_env)
: ReductionPass(target_env) {}
OperandToDominatingIdReductionOpportunityFinder() = default;
~OperandToDominatingIdReductionPass() override = default;
~OperandToDominatingIdReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -56,4 +53,4 @@ class OperandToDominatingIdReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/operand_to_undef_reduction_pass.h"
#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
#include "source/opt/instruction.h"
#include "source/reduce/change_operand_to_undef_reduction_opportunity.h"
@@ -23,7 +23,7 @@ namespace reduce {
using namespace opt;
std::vector<std::unique_ptr<ReductionOpportunity>>
OperandToUndefReductionPass::GetAvailableOpportunities(
OperandToUndefReductionOpportunityFinder::GetAvailableOpportunities(
IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
@@ -86,8 +86,8 @@ OperandToUndefReductionPass::GetAvailableOpportunities(
return result;
}
std::string OperandToUndefReductionPass::GetName() const {
return "OperandToUndefReductionPass";
std::string OperandToUndefReductionOpportunityFinder::GetName() const {
return "OperandToUndefReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,27 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_PASS_H_
#define SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_
#include "source/reduce/reduction_pass.h"
#include "source/reduce/reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A reduction pass for replacing id operands of instructions with ids of undef.
class OperandToUndefReductionPass : public ReductionPass {
// A finder of opportunities to replace id operands of instructions with ids of
// undef.
class OperandToUndefReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit OperandToUndefReductionPass(const spv_target_env target_env)
: ReductionPass(target_env) {}
OperandToUndefReductionOpportunityFinder() = default;
~OperandToUndefReductionPass() override = default;
~OperandToUndefReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -42,4 +40,4 @@ class OperandToUndefReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -16,8 +16,8 @@
#include <functional>
#include <string>
#include "source/reduce/change_operand_reduction_opportunity.h"
#include "source/reduce/operand_to_const_reduction_pass.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/reduction_pass.h"
#include "source/reduce/remove_instruction_reduction_opportunity.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"

View File

@@ -141,8 +141,9 @@ Reducer::ReductionResultStatus Reducer::Run(
}
void Reducer::AddReductionPass(
std::unique_ptr<ReductionPass>&& reduction_pass) {
impl_->passes.push_back(std::move(reduction_pass));
std::unique_ptr<ReductionOpportunityFinder>&& finder) {
impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
impl_->target_env, std::move(finder)));
}
bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,

View File

@@ -75,9 +75,9 @@ class Reducer {
void SetInterestingnessFunction(
InterestingnessFunction interestingness_function);
// Adds a reduction pass to the sequence of passes that will be iterated
// over.
void AddReductionPass(std::unique_ptr<ReductionPass>&& reduction_pass);
// Adds a reduction pass based on the given finder to the sequence of passes
// that will be iterated over.
void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
// Reduces the given SPIR-V module |binary_out|.
// The reduced binary ends up in |binary_out|.

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_
#include "reduction_opportunity.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace reduce {
// Abstract class for finding opportunities for reducing a SPIR-V module.
class ReductionOpportunityFinder {
public:
ReductionOpportunityFinder() = default;
virtual ~ReductionOpportunityFinder() = default;
// Finds and returns the reduction opportunities relevant to this pass that
// could be applied to the given SPIR-V module.
virtual std::vector<std::unique_ptr<ReductionOpportunity>>
GetAvailableOpportunities(opt::IRContext* context) const = 0;
// Provides a name for the finder.
virtual std::string GetName() const = 0;
};
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -34,7 +34,7 @@ std::vector<uint32_t> ReductionPass::TryApplyReduction(
assert(context);
std::vector<std::unique_ptr<ReductionOpportunity>> opportunities =
GetAvailableOpportunities(context.get());
finder_->GetAvailableOpportunities(context.get());
if (!is_initialized_) {
is_initialized_ = true;
@@ -82,5 +82,7 @@ bool ReductionPass::ReachedMinimumGranularity() const {
return granularity_ == 1;
}
std::string ReductionPass::GetName() const { return finder_->GetName(); }
} // namespace reduce
} // namespace spvtools

View File

@@ -17,7 +17,7 @@
#include "spirv-tools/libspirv.hpp"
#include "reduction_opportunity.h"
#include "reduction_opportunity_finder.h"
#include "source/opt/ir_context.h"
namespace spvtools {
@@ -32,12 +32,14 @@ namespace reduce {
// again, until the minimum granularity is reached.
class ReductionPass {
public:
// Constructs a reduction pass with a given target environment, |target_env|.
// Initially the pass is uninitialized.
explicit ReductionPass(const spv_target_env target_env)
: target_env_(target_env), is_initialized_(false) {}
virtual ~ReductionPass() = default;
// Constructs a reduction pass with a given target environment, |target_env|,
// and a given finder of reduction opportunities, |finder|. Initially the
// pass is uninitialized.
explicit ReductionPass(const spv_target_env target_env,
std::unique_ptr<ReductionOpportunityFinder> finder)
: target_env_(target_env),
finder_(std::move(finder)),
is_initialized_(false) {}
// Applies the reduction pass to the given binary.
std::vector<uint32_t> TryApplyReduction(const std::vector<uint32_t>& binary);
@@ -49,18 +51,13 @@ class ReductionPass {
// applied has reached a minimum.
bool ReachedMinimumGranularity() const;
// Returns the name of the reduction pass (useful for monitoring reduction
// progress).
virtual std::string GetName() const = 0;
protected:
// Finds and returns the reduction opportunities relevant to this pass that
// could be applied to the given SPIR-V module.
virtual std::vector<std::unique_ptr<ReductionOpportunity>>
GetAvailableOpportunities(opt::IRContext* context) const = 0;
// Returns the name associated with this reduction pass (based on its
// associated finder).
std::string GetName() const;
private:
const spv_target_env target_env_;
const std::unique_ptr<ReductionOpportunityFinder> finder_;
MessageConsumer consumer_;
bool is_initialized_;
uint32_t index_;

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "remove_opname_instruction_reduction_pass.h"
#include "remove_opname_instruction_reduction_opportunity_finder.h"
#include "remove_instruction_reduction_opportunity.h"
#include "source/opcode.h"
#include "source/opt/instruction.h"
@@ -23,7 +23,7 @@ namespace reduce {
using namespace opt;
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveOpNameInstructionReductionPass::GetAvailableOpportunities(
RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
@@ -36,8 +36,8 @@ RemoveOpNameInstructionReductionPass::GetAvailableOpportunities(
return result;
}
std::string RemoveOpNameInstructionReductionPass::GetName() const {
return "RemoveOpNameInstructionReductionPass";
std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const {
return "RemoveOpNameInstructionReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,30 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_PASS_H_
#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#include "reduction_pass.h"
#include "reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A reduction pass for removing OpName instructions. As well as making the
// module smaller, removing an OpName instruction may create opportunities
// A finder for opportunities to remove OpName instructions. As well as making
// the module smaller, removing an OpName instruction may create opportunities
// for subsequently removing the instructions that create the ids to which the
// OpName applies.
class RemoveOpNameInstructionReductionPass : public ReductionPass {
class RemoveOpNameInstructionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit RemoveOpNameInstructionReductionPass(const spv_target_env target_env)
: ReductionPass(target_env) {}
RemoveOpNameInstructionReductionOpportunityFinder() = default;
~RemoveOpNameInstructionReductionPass() override = default;
~RemoveOpNameInstructionReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -45,4 +42,4 @@ class RemoveOpNameInstructionReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REMOVE_OpName_INSTRUCTION_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "remove_unreferenced_instruction_reduction_pass.h"
#include "remove_unreferenced_instruction_reduction_opportunity_finder.h"
#include "remove_instruction_reduction_opportunity.h"
#include "source/opcode.h"
#include "source/opt/instruction.h"
@@ -23,8 +23,8 @@ namespace reduce {
using namespace opt;
std::vector<std::unique_ptr<ReductionOpportunity>>
RemoveUnreferencedInstructionReductionPass::GetAvailableOpportunities(
opt::IRContext* context) const {
RemoveUnreferencedInstructionReductionOpportunityFinder::
GetAvailableOpportunities(opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
for (auto& function : *context->module()) {
@@ -52,8 +52,9 @@ RemoveUnreferencedInstructionReductionPass::GetAvailableOpportunities(
return result;
}
std::string RemoveUnreferencedInstructionReductionPass::GetName() const {
return "RemoveUnreferencedInstructionReductionPass";
std::string RemoveUnreferencedInstructionReductionOpportunityFinder::GetName()
const {
return "RemoveUnreferencedInstructionReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,32 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
#include "reduction_pass.h"
#include "reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// A reduction pass for removing non-control-flow instructions in blocks in
// cases where the instruction's id is not referenced. As well as making the
// A finder for opportunities to remove non-control-flow instructions in blocks
// in cases where the instruction's id is not referenced. As well as making the
// module smaller, removing an instruction that references particular ids may
// create opportunities for subsequently removing the instructions that
// generated those ids.
class RemoveUnreferencedInstructionReductionPass : public ReductionPass {
class RemoveUnreferencedInstructionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit RemoveUnreferencedInstructionReductionPass(
const spv_target_env target_env)
: ReductionPass(target_env) {}
RemoveUnreferencedInstructionReductionOpportunityFinder() = default;
~RemoveUnreferencedInstructionReductionPass() override = default;
~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -47,4 +43,4 @@ class RemoveUnreferencedInstructionReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "structured_loop_to_selection_reduction_pass.h"
#include "structured_loop_to_selection_reduction_opportunity_finder.h"
#include "structured_loop_to_selection_reduction_opportunity.h"
namespace spvtools {
@@ -26,7 +26,7 @@ const uint32_t kContinueNodeIndex = 1;
} // namespace
std::vector<std::unique_ptr<ReductionOpportunity>>
StructuredLoopToSelectionReductionPass::GetAvailableOpportunities(
StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities(
opt::IRContext* context) const {
std::vector<std::unique_ptr<ReductionOpportunity>> result;
@@ -87,8 +87,9 @@ StructuredLoopToSelectionReductionPass::GetAvailableOpportunities(
return result;
}
std::string StructuredLoopToSelectionReductionPass::GetName() const {
return "StructuredLoopToSelectionReductionPass";
std::string StructuredLoopToSelectionReductionOpportunityFinder::GetName()
const {
return "StructuredLoopToSelectionReductionOpportunityFinder";
}
} // namespace reduce

View File

@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_PASS_H_
#define SOURCE_REDUCE_CUT_LOOP_REDUCTION_PASS_H_
#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H
#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H
#include "reduction_pass.h"
#include "reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
// Turns structured loops into selections, generalizing from a human-writable
// language the idea of turning a loop:
// A finder for opportunities to turn structured loops into selections,
// generalizing from a human-writable language the idea of turning a loop:
//
// while (c) {
// body;
@@ -33,22 +33,18 @@ namespace reduce {
// body;
// }
//
// The pass results in continue constructs of transformed loops becoming
// unreachable; another pass for eliminating blocks may end up being able to
// remove them.
class StructuredLoopToSelectionReductionPass : public ReductionPass {
// Applying such opportunities results in continue constructs of transformed
// loops becoming unreachable, so that it may be possible to remove them
// subsequently.
class StructuredLoopToSelectionReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit StructuredLoopToSelectionReductionPass(
const spv_target_env target_env)
: ReductionPass(target_env) {}
StructuredLoopToSelectionReductionOpportunityFinder() = default;
~StructuredLoopToSelectionReductionPass() override = default;
~StructuredLoopToSelectionReductionOpportunityFinder() override = default;
std::string GetName() const final;
protected:
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
opt::IRContext* context) const final;
@@ -58,4 +54,4 @@ class StructuredLoopToSelectionReductionPass : public ReductionPass {
} // namespace reduce
} // namespace spvtools
#endif // SOURCE_REDUCE_CUT_LOOP_REDUCTION_PASS_H_
#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H

View File

@@ -248,3 +248,43 @@ bool spvIsWebGPUEnv(spv_target_env env) {
}
return false;
}
bool spvIsVulkanOrWebGPUEnv(spv_target_env env) {
return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env);
}
std::string spvLogStringForEnv(spv_target_env env) {
switch (env) {
case SPV_ENV_OPENCL_1_2:
case SPV_ENV_OPENCL_2_0:
case SPV_ENV_OPENCL_2_1:
case SPV_ENV_OPENCL_2_2:
case SPV_ENV_OPENCL_EMBEDDED_1_2:
case SPV_ENV_OPENCL_EMBEDDED_2_0:
case SPV_ENV_OPENCL_EMBEDDED_2_1:
case SPV_ENV_OPENCL_EMBEDDED_2_2: {
return "OpenCL";
}
case SPV_ENV_OPENGL_4_0:
case SPV_ENV_OPENGL_4_1:
case SPV_ENV_OPENGL_4_2:
case SPV_ENV_OPENGL_4_3:
case SPV_ENV_OPENGL_4_5: {
return "OpenGL";
}
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1: {
return "Vulkan";
}
case SPV_ENV_WEBGPU_0: {
return "WebGPU";
}
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3: {
return "Universal";
}
}
return "Unknown";
}

View File

@@ -15,6 +15,8 @@
#ifndef SOURCE_SPIRV_TARGET_ENV_H_
#define SOURCE_SPIRV_TARGET_ENV_H_
#include <string>
#include "spirv-tools/libspirv.h"
// Parses s into *env and returns true if successful. If unparsable, returns
@@ -30,7 +32,14 @@ bool spvIsOpenCLEnv(spv_target_env env);
// Returns true if |env| is an WEBGPU environment, false otherwise.
bool spvIsWebGPUEnv(spv_target_env env);
// Returns true if |env| is a VULKAN or WEBGPU environment, false otherwise.
bool spvIsVulkanOrWebGPUEnv(spv_target_env env);
// Returns the version number for the given SPIR-V target environment.
uint32_t spvVersionForTargetEnv(spv_target_env env);
// Returns a string to use in logging messages that indicates the class of
// environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc.
std::string spvLogStringForEnv(spv_target_env env);
#endif // SOURCE_SPIRV_TARGET_ENV_H_

View File

@@ -249,8 +249,7 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) {
// For Vulkan and WebGPU, the static function-call graph for an entry point
// must not contain cycles.
if (spvIsWebGPUEnv(_.context()->target_env) ||
spvIsVulkanEnv(_.context()->target_env)) {
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
if (_.recursive_entry_points().find(entry_point) !=
_.recursive_entry_points().end()) {
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point))

View File

@@ -1617,6 +1617,46 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference(
}
}
if (spvIsWebGPUEnv(_.context()->target_env)) {
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
if (storage_class != SpvStorageClassMax &&
storage_class != SpvStorageClassOutput) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< "WebGPU spec allows BuiltIn Position to be only used for "
"variables with Output storage class. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst)
<< " " << GetStorageClassDesc(referenced_from_inst);
}
for (const SpvExecutionModel execution_model : execution_models_) {
switch (execution_model) {
case SpvExecutionModelVertex: {
if (spv_result_t error = ValidateF32Vec(
decoration, built_in_inst, 4,
[this, &referenced_from_inst](
const std::string& message) -> spv_result_t {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< "According to the WebGPU spec BuiltIn Position "
"variable needs to be a 4-component 32-bit float "
"vector. "
<< message;
})) {
return error;
}
break;
}
default: {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< "WebGPU spec allows BuiltIn Position to be used only "
"with the Vertex execution model. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst, execution_model);
}
}
}
}
if (function_id_ == 0) {
// Propagate this rule to all dependant ids in the global scope.
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
@@ -2656,14 +2696,15 @@ spv_result_t BuiltInsValidator::Run() {
// Validates correctness of built-in variables.
spv_result_t ValidateBuiltIns(ValidationState_t& _) {
if (!spvIsVulkanEnv(_.context()->target_env)) {
// Early return. All currently implemented rules are based on Vulkan spec.
if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
// Early return. All currently implemented rules are based on Vulkan or
// WebGPU spec.
//
// TODO: If you are adding validation rules for environments other than
// Vulkan (or general rules which are not environment independent), then you
// need to modify or remove this condition. Consider also adding early
// returns into BuiltIn-specific rules, so that the system doesn't spawn new
// rules which don't do anything.
// Vulkan or WebGPU (or general rules which are not environment
// independent), then you need to modify or remove this condition. Consider
// also adding early returns into BuiltIn-specific rules, so that the system
// doesn't spawn new rules which don't do anything.
return SPV_SUCCESS;
}

View File

@@ -112,6 +112,19 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) {
return SPV_SUCCESS;
}
spv_result_t ValidateBranch(ValidationState_t& _, const Instruction* inst) {
// target operands must be OpLabel
const auto id = inst->GetOperandAs<uint32_t>(0);
const auto target = _.FindDef(id);
if (!target || SpvOpLabel != target->opcode()) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "'Target Label' operands for OpBranch must be the ID "
"of an OpLabel instruction";
}
return SPV_SUCCESS;
}
spv_result_t ValidateBranchConditional(ValidationState_t& _,
const Instruction* inst) {
// num_operands is either 3 or 5 --- if 5, the last two need to be literal
@@ -155,6 +168,26 @@ spv_result_t ValidateBranchConditional(ValidationState_t& _,
return SPV_SUCCESS;
}
spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) {
const auto num_operands = inst->operands().size();
// At least two operands (selector, default), any more than that are
// literal/target.
// target operands must be OpLabel
for (size_t i = 2; i < num_operands; i += 2) {
// literal, id
const auto id = inst->GetOperandAs<uint32_t>(i + 1);
const auto target = _.FindDef(id);
if (!target || SpvOpLabel != target->opcode()) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "'Target Label' operands for OpSwitch must be IDs of an "
"OpLabel instruction";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateReturnValue(ValidationState_t& _,
const Instruction* inst) {
const auto value_id = inst->GetOperandAs<uint32_t>(0);
@@ -764,12 +797,18 @@ spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
case SpvOpPhi:
if (auto error = ValidatePhi(_, inst)) return error;
break;
case SpvOpBranch:
if (auto error = ValidateBranch(_, inst)) return error;
break;
case SpvOpBranchConditional:
if (auto error = ValidateBranchConditional(_, inst)) return error;
break;
case SpvOpReturnValue:
if (auto error = ValidateReturnValue(_, inst)) return error;
break;
case SpvOpSwitch:
if (auto error = ValidateSwitch(_, inst)) return error;
break;
default:
break;
}

View File

@@ -214,6 +214,21 @@ spv_result_t ValidateStruct(ValidationState_t& _, const Instruction* inst) {
return SPV_SUCCESS;
}
// Validates that any undefined type of the array is a forward pointer.
// It is valid to declare a forward pointer, and use its <id> as the element
// type of the array.
spv_result_t ValidateArray(ValidationState_t& _, const Instruction* inst) {
auto element_type_id = inst->GetOperandAs<const uint32_t>(1);
auto element_type_instruction = _.FindDef(element_type_id);
if (element_type_instruction == nullptr &&
!_.IsForwardPointer(element_type_id)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Forward reference operands in an OpTypeArray must first be "
"declared using OpTypeForwardPointer.";
}
return SPV_SUCCESS;
}
} // namespace
// Validates that Data Rules are followed according to the specifications.
@@ -256,6 +271,10 @@ spv_result_t DataRulesPass(ValidationState_t& _, const Instruction* inst) {
if (auto error = ValidateStruct(_, inst)) return error;
break;
}
case SpvOpTypeArray: {
if (auto error = ValidateArray(_, inst)) return error;
break;
}
// TODO(ehsan): add more data rules validation here.
default: { break; }
}

View File

@@ -379,6 +379,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
// or row major-ness.
spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
const char* decoration_str, bool blockRules,
uint32_t incoming_offset,
MemberConstraints& constraints,
ValidationState_t& vstate) {
if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
@@ -429,7 +430,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
}
}
}
member_offsets.push_back(MemberOffsetPair{memberIdx, offset});
member_offsets.push_back(
MemberOffsetPair{memberIdx, incoming_offset + offset});
}
std::stable_sort(
member_offsets.begin(), member_offsets.end(),
@@ -493,9 +495,9 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
// Check struct members recursively.
spv_result_t recursive_status = SPV_SUCCESS;
if (SpvOpTypeStruct == opcode &&
SPV_SUCCESS != (recursive_status =
checkLayout(id, storage_class_str, decoration_str,
blockRules, constraints, vstate)))
SPV_SUCCESS != (recursive_status = checkLayout(
id, storage_class_str, decoration_str, blockRules,
offset, constraints, vstate)))
return recursive_status;
// Check matrix stride.
if (SpvOpTypeMatrix == opcode) {
@@ -507,23 +509,53 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
<< " not satisfying alignment to " << alignment;
}
}
// Check arrays and runtime arrays.
if (SpvOpTypeArray == opcode || SpvOpTypeRuntimeArray == opcode) {
const auto typeId = inst->word(2);
const auto arrayInst = vstate.FindDef(typeId);
if (SpvOpTypeStruct == arrayInst->opcode() &&
SPV_SUCCESS != (recursive_status = checkLayout(
typeId, storage_class_str, decoration_str,
blockRules, constraints, vstate)))
return recursive_status;
// Check arrays and runtime arrays recursively.
auto array_inst = inst;
auto array_alignment = alignment;
while (array_inst->opcode() == SpvOpTypeArray ||
array_inst->opcode() == SpvOpTypeRuntimeArray) {
const auto typeId = array_inst->word(2);
const auto element_inst = vstate.FindDef(typeId);
// Check array stride.
for (auto& decoration : vstate.id_decorations(id)) {
if (SpvDecorationArrayStride == decoration.dec_type() &&
!IsAlignedTo(decoration.params()[0], alignment))
return fail(memberIdx)
<< "is an array with stride " << decoration.params()[0]
<< " not satisfying alignment to " << alignment;
auto array_stride = 0;
for (auto& decoration : vstate.id_decorations(array_inst->id())) {
if (SpvDecorationArrayStride == decoration.dec_type()) {
array_stride = decoration.params()[0];
if (!IsAlignedTo(array_stride, array_alignment))
return fail(memberIdx)
<< "contains an array with stride " << decoration.params()[0]
<< " not satisfying alignment to " << alignment;
}
}
bool is_int32 = false;
bool is_const = false;
uint32_t num_elements = 0;
if (array_inst->opcode() == SpvOpTypeArray) {
std::tie(is_int32, is_const, num_elements) =
vstate.EvalInt32IfConst(array_inst->word(3));
}
num_elements = std::max(1u, num_elements);
// Check each element recursively if it is a struct. There is a
// limitation to this check if the array size is a spec constant or is a
// runtime array then we will only check a single element. This means
// some improper straddles might be missed.
for (uint32_t i = 0; i < num_elements; ++i) {
uint32_t next_offset = i * array_stride + offset;
if (SpvOpTypeStruct == element_inst->opcode() &&
SPV_SUCCESS != (recursive_status = checkLayout(
typeId, storage_class_str, decoration_str,
blockRules, next_offset, constraints, vstate)))
return recursive_status;
}
// Proceed to the element in case it is an array.
array_inst = element_inst;
array_alignment = scalar_block_layout
? getScalarAlignment(array_inst->id(), vstate)
: getBaseAlignment(array_inst->id(), blockRules,
constraint, constraints, vstate);
}
nextValidOffset = offset + size;
if (!scalar_block_layout && blockRules &&
@@ -859,8 +891,15 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
const auto ptrInst = vstate.FindDef(words[1]);
assert(SpvOpTypePointer == ptrInst->opcode());
const auto id = ptrInst->words()[3];
if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) continue;
auto id = ptrInst->words()[3];
auto id_inst = vstate.FindDef(id);
// Jump through one level of arraying.
if (id_inst->opcode() == SpvOpTypeArray ||
id_inst->opcode() == SpvOpTypeRuntimeArray) {
id = id_inst->GetOperandAs<uint32_t>(1u);
id_inst = vstate.FindDef(id);
}
if (SpvOpTypeStruct != id_inst->opcode()) continue;
MemberConstraints constraints;
ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
vstate);
@@ -942,12 +981,12 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
"decorations.";
} else if (blockRules &&
(SPV_SUCCESS != (recursive_status = checkLayout(
id, sc_str, deco_str, true,
id, sc_str, deco_str, true, 0,
constraints, vstate)))) {
return recursive_status;
} else if (bufferRules &&
(SPV_SUCCESS != (recursive_status = checkLayout(
id, sc_str, deco_str, false,
id, sc_str, deco_str, false, 0,
constraints, vstate)))) {
return recursive_status;
}
@@ -959,42 +998,64 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
return SPV_SUCCESS;
}
// Returns true if |decoration| cannot be applied to the same id more than once.
bool AtMostOncePerId(SpvDecoration decoration) {
return decoration == SpvDecorationArrayStride;
}
// Returns true if |decoration| cannot be applied to the same member more than
// once.
bool AtMostOncePerMember(SpvDecoration decoration) {
switch (decoration) {
case SpvDecorationOffset:
case SpvDecorationMatrixStride:
case SpvDecorationRowMajor:
case SpvDecorationColMajor:
return true;
default:
return false;
}
}
// Returns the string name for |decoration|.
const char* GetDecorationName(SpvDecoration decoration) {
switch (decoration) {
case SpvDecorationArrayStride:
return "ArrayStride";
case SpvDecorationOffset:
return "Offset";
case SpvDecorationMatrixStride:
return "MatrixStride";
case SpvDecorationRowMajor:
return "RowMajor";
case SpvDecorationColMajor:
return "ColMajor";
case SpvDecorationBlock:
return "Block";
case SpvDecorationBufferBlock:
return "BufferBlock";
default:
return "";
}
}
spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
using AtMostOnceSet = std::unordered_set<SpvDecoration, SpvDecorationHash>;
using MutuallyExclusiveSets =
std::vector<std::unordered_set<SpvDecoration, SpvDecorationHash>>;
using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
using DecorationNameTable =
std::unordered_map<SpvDecoration, std::string, SpvDecorationHash>;
static const auto* const at_most_once_per_id = new AtMostOnceSet{
SpvDecorationArrayStride,
};
static const auto* const at_most_once_per_member = new AtMostOnceSet{
SpvDecorationOffset,
SpvDecorationMatrixStride,
SpvDecorationRowMajor,
SpvDecorationColMajor,
};
static const auto* const mutually_exclusive_per_id =
new MutuallyExclusiveSets{
{SpvDecorationBlock, SpvDecorationBufferBlock},
};
static const auto* const mutually_exclusive_per_member =
new MutuallyExclusiveSets{
{SpvDecorationRowMajor, SpvDecorationColMajor},
};
// For printing the decoration name.
static const auto* const decoration_name = new DecorationNameTable{
{SpvDecorationArrayStride, "ArrayStride"},
{SpvDecorationOffset, "Offset"},
{SpvDecorationMatrixStride, "MatrixStride"},
{SpvDecorationRowMajor, "RowMajor"},
{SpvDecorationColMajor, "ColMajor"},
{SpvDecorationBlock, "Block"},
{SpvDecorationBufferBlock, "BufferBlock"},
};
// An Array of pairs where the decorations in the pair cannot both be applied
// to the same id.
static const SpvDecoration mutually_exclusive_per_id[][2] = {
{SpvDecorationBlock, SpvDecorationBufferBlock}};
static const auto num_mutually_exclusive_per_id_pairs =
sizeof(mutually_exclusive_per_id) / (2 * sizeof(SpvDecoration));
// An Array of pairs where the decorations in the pair cannot both be applied
// to the same member.
static const SpvDecoration mutually_exclusive_per_member[][2] = {
{SpvDecorationRowMajor, SpvDecorationColMajor}};
static const auto num_mutually_exclusive_per_mem_pairs =
sizeof(mutually_exclusive_per_member) / (2 * sizeof(SpvDecoration));
std::set<PerIDKey> seen_per_id;
std::set<PerMemberKey> seen_per_member;
@@ -1006,26 +1067,31 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
const auto dec_type = static_cast<SpvDecoration>(words[2]);
const auto k = PerIDKey(dec_type, id);
const auto already_used = !seen_per_id.insert(k).second;
if (already_used &&
at_most_once_per_id->find(dec_type) != at_most_once_per_id->end()) {
if (already_used && AtMostOncePerId(dec_type)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with "
<< decoration_name->at(dec_type)
<< GetDecorationName(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
// an ID.
for (const auto& s : *mutually_exclusive_per_id) {
if (s.find(dec_type) == s.end()) continue;
for (auto excl_dec_type : s) {
if (excl_dec_type == dec_type) continue;
const auto excl_k = PerIDKey(excl_dec_type, id);
if (seen_per_id.find(excl_k) != seen_per_id.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with both "
<< decoration_name->at(dec_type) << " and "
<< decoration_name->at(excl_dec_type) << " is not allowed.";
}
for (uint32_t pair_idx = 0;
pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) {
SpvDecoration excl_dec_type = SpvDecorationMax;
if (mutually_exclusive_per_id[pair_idx][0] == dec_type) {
excl_dec_type = mutually_exclusive_per_id[pair_idx][1];
} else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) {
excl_dec_type = mutually_exclusive_per_id[pair_idx][0];
} else {
continue;
}
const auto excl_k = PerIDKey(excl_dec_type, id);
if (seen_per_id.find(excl_k) != seen_per_id.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with both "
<< GetDecorationName(dec_type) << " and "
<< GetDecorationName(excl_dec_type) << " is not allowed.";
}
}
} else if (SpvOpMemberDecorate == inst.opcode()) {
@@ -1034,27 +1100,32 @@ spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
const auto dec_type = static_cast<SpvDecoration>(words[3]);
const auto k = PerMemberKey(dec_type, id, member_id);
const auto already_used = !seen_per_member.insert(k).second;
if (already_used && at_most_once_per_member->find(dec_type) !=
at_most_once_per_member->end()) {
if (already_used && AtMostOncePerMember(dec_type)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
<< "' decorated with " << decoration_name->at(dec_type)
<< "' decorated with " << GetDecorationName(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
// a (ID, member) tuple.
for (const auto& s : *mutually_exclusive_per_member) {
if (s.find(dec_type) == s.end()) continue;
for (auto excl_dec_type : s) {
if (excl_dec_type == dec_type) continue;
const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
if (seen_per_member.find(excl_k) != seen_per_member.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
<< "' decorated with both " << decoration_name->at(dec_type)
<< " and " << decoration_name->at(excl_dec_type)
<< " is not allowed.";
}
for (uint32_t pair_idx = 0;
pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) {
SpvDecoration excl_dec_type = SpvDecorationMax;
if (mutually_exclusive_per_member[pair_idx][0] == dec_type) {
excl_dec_type = mutually_exclusive_per_member[pair_idx][1];
} else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) {
excl_dec_type = mutually_exclusive_per_member[pair_idx][0];
} else {
continue;
}
const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
if (seen_per_member.find(excl_k) != seen_per_member.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
<< "' decorated with both " << GetDecorationName(dec_type)
<< " and " << GetDecorationName(excl_dec_type)
<< " is not allowed.";
}
}
}

View File

@@ -515,23 +515,13 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput &&
storage_class != SpvStorageClassPrivate &&
storage_class != SpvStorageClassFunction) {
if (spvIsVulkanEnv(_.context()->target_env)) {
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable, <id> '" << _.getIdName(inst->id())
<< "', has a disallowed initializer & storage class "
<< "combination.\n"
<< "From Vulkan spec, Appendix A:\n"
<< "Variable declarations that include initializers must have "
<< "one of the following storage classes: Output, Private, or "
<< "Function";
}
if (spvIsWebGPUEnv(_.context()->target_env)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVariable, <id> '" << _.getIdName(inst->id())
<< "', has a disallowed initializer & storage class "
<< "combination.\n"
<< "From WebGPU execution environment spec:\n"
<< "From " << spvLogStringForEnv(_.context()->target_env)
<< " spec:\n"
<< "Variable declarations that include initializers must have "
<< "one of the following storage classes: Output, Private, or "
<< "Function";

View File

@@ -107,14 +107,12 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
<< "' is a void type.";
}
if ((spvIsVulkanEnv(_.context()->target_env) ||
spvIsWebGPUEnv(_.context()->target_env)) &&
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
const char* env_text =
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
<< "' is not valid in " << env_text << " environment.";
<< "' is not valid in "
<< spvLogStringForEnv(_.context()->target_env) << " environments.";
}
const auto length_index = 2;
@@ -172,15 +170,12 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
<< _.getIdName(element_id) << "' is a void type.";
}
if ((spvIsVulkanEnv(_.context()->target_env) ||
spvIsWebGPUEnv(_.context()->target_env)) &&
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
element_type->opcode() == SpvOpTypeRuntimeArray) {
const char* env_text =
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpTypeRuntimeArray Element Type <id> '"
<< _.getIdName(element_id) << "' is not valid in " << env_text
<< " environment.";
<< _.getIdName(element_id) << "' is not valid in "
<< spvLogStringForEnv(_.context()->target_env) << " environments.";
}
return SPV_SUCCESS;
@@ -229,17 +224,15 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
}
}
if ((spvIsVulkanEnv(_.context()->target_env) ||
spvIsWebGPUEnv(_.context()->target_env)) &&
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
member_type->opcode() == SpvOpTypeRuntimeArray) {
const bool is_last_member =
member_type_index == inst->operands().size() - 1;
if (!is_last_member) {
const char* env_text =
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "In " << env_text << ", OpTypeRuntimeArray must only be used "
<< "for the last member of an OpTypeStruct";
<< "In " << spvLogStringForEnv(_.context()->target_env)
<< ", OpTypeRuntimeArray must only be used for the last member "
"of an OpTypeStruct";
}
}
}

View File

@@ -528,6 +528,30 @@ TEST_F(CodeSinkTest, MoveWithAtomicWithoutSync) {
EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
}
TEST_F(CodeSinkTest, DecorationOnLoad) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main" %2
OpDecorate %3 RelaxedPrecision
%void = OpTypeVoid
%5 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%2 = OpVariable %_ptr_Input_float Input
%1 = OpFunction %void None %5
%8 = OpLabel
%3 = OpLoad %float %2
OpReturn
OpFunctionEnd
)";
// We just want to make sure the code does not crash.
auto result = SinglePassRunAndDisassemble<CodeSinkingPass>(
text, /* skip_nop = */ true, /* do_validation = */ true);
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -13,15 +13,15 @@
# limitations under the License.
add_spvtools_unittest(TARGET reduce
SRCS operand_to_constant_reduction_pass_test.cpp
operand_to_undef_reduction_pass_test.cpp
operand_to_dominating_id_reduction_pass_test.cpp
SRCS operand_to_constant_test.cpp
operand_to_undef_test.cpp
operand_to_dominating_id_test.cpp
reduce_test_util.cpp
reduce_test_util.h
reducer_test.cpp
remove_opname_instruction_reduction_pass_test.cpp
remove_unreferenced_instruction_reduction_pass_test.cpp
structured_loop_to_selection_reduction_pass_test.cpp
remove_opname_instruction_test.cpp
remove_unreferenced_instruction_test.cpp
structured_loop_to_selection_test.cpp
validation_during_reduction_test.cpp
LIBS SPIRV-Tools-reduce
)

View File

@@ -14,7 +14,7 @@
#include "reduce_test_util.h"
#include "source/opt/build_module.h"
#include "source/reduce/operand_to_const_reduction_pass.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
@@ -97,8 +97,9 @@ TEST(OperandToConstantReductionPassTest, BasicCheck) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto pass = TestSubclass<OperandToConstReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
ASSERT_EQ(17, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@@ -146,8 +147,9 @@ TEST(OperandToConstantReductionPassTest, WithCalledFunction) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto pass = TestSubclass<OperandToConstReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops =
OperandToConstReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
ASSERT_EQ(0, ops.size());
}

View File

@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/operand_to_dominating_id_reduction_pass.h"
#include "reduce_test_util.h"
#include "source/opt/build_module.h"
#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
@@ -53,8 +53,8 @@ TEST(OperandToDominatingIdReductionPassTest, BasicCheck) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto pass = TestSubclass<OperandToDominatingIdReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = OperandToDominatingIdReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(10, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();

View File

@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/operand_to_undef_reduction_pass.h"
#include "source/opt/build_module.h"
#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
#include "test/reduce/reduce_test_util.h"
namespace spvtools {
@@ -163,8 +163,9 @@ TEST(OperandToUndefReductionPassTest, BasicCheck) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto pass = TestSubclass<OperandToUndefReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops =
OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
ASSERT_EQ(10, ops.size());
@@ -216,8 +217,9 @@ TEST(OperandToUndefReductionPassTest, WithCalledFunction) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kReduceAssembleOption);
const auto pass = TestSubclass<OperandToUndefReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops =
OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities(
context.get());
ASSERT_EQ(0, ops.size());
}

View File

@@ -24,23 +24,6 @@
namespace spvtools {
namespace reduce {
// A helper class that subclasses a given reduction pass class in order to
// provide a wrapper for its protected methods.
template <class ReductionPassT>
class TestSubclass : public ReductionPassT {
public:
// Creates an instance of the reduction pass subclass with respect to target
// environment |env|.
explicit TestSubclass(const spv_target_env env) : ReductionPassT(env) {}
~TestSubclass() = default;
// A wrapper for GetAvailableOpportunities(...)
std::vector<std::unique_ptr<ReductionOpportunity>>
WrapGetAvailableOpportunities(opt::IRContext* context) const {
return ReductionPassT::GetAvailableOpportunities(context);
}
};
// Checks whether the given binaries are bit-wise equal.
void CheckEqual(spv_target_env env,
const std::vector<uint32_t>& expected_binary,

View File

@@ -14,10 +14,10 @@
#include "reduce_test_util.h"
#include "source/reduce/operand_to_const_reduction_pass.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
#include "source/reduce/reducer.h"
#include "source/reduce/remove_opname_instruction_reduction_pass.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
@@ -217,9 +217,10 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
[&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
return ping_pong_interesting.IsInteresting(binary);
});
reducer.AddReductionPass(MakeUnique<OperandToConstReductionPass>(env));
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionPass>(env));
MakeUnique<OperandToConstReductionOpportunityFinder>());
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);
@@ -288,9 +289,9 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
return ping_pong_interesting.IsInteresting(binary);
});
reducer.AddReductionPass(
MakeUnique<RemoveOpNameInstructionReductionPass>(env));
MakeUnique<RemoveOpNameInstructionReductionOpportunityFinder>());
reducer.AddReductionPass(
MakeUnique<RemoveUnreferencedInstructionReductionPass>(env));
MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);

View File

@@ -16,7 +16,8 @@
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/remove_opname_instruction_reduction_pass.h"
#include "source/reduce/reduction_pass.h"
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
@@ -42,8 +43,8 @@ TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, source, kReduceAssembleOption);
const auto pass = TestSubclass<RemoveOpNameInstructionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
@@ -76,8 +77,8 @@ TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto pass = TestSubclass<RemoveOpNameInstructionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@@ -126,14 +127,14 @@ TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
const std::string expected = prologue + epilogue;
const auto env = SPV_ENV_UNIVERSAL_1_3;
auto pass = TestSubclass<RemoveOpNameInstructionReductionPass>(env);
{
// Check the right number of opportunities is detected
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(5, ops.size());
}
@@ -142,7 +143,11 @@ TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
std::vector<uint32_t> binary;
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
auto reduced_binary = pass.TryApplyReduction(binary);
auto reduced_binary =
ReductionPass(env,
spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>())
.TryApplyReduction(binary);
CheckEqual(env, expected, reduced_binary);
}
}
@@ -190,14 +195,14 @@ TEST(RemoveOpnameInstructionReductionPassTest,
const std::string expected = prologue + epilogue;
const auto env = SPV_ENV_UNIVERSAL_1_3;
auto pass = TestSubclass<RemoveOpNameInstructionReductionPass>(env);
{
// Check the right number of opportunities is detected
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(6, ops.size());
}
@@ -206,7 +211,11 @@ TEST(RemoveOpnameInstructionReductionPassTest,
std::vector<uint32_t> binary;
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
auto reduced_binary = pass.TryApplyReduction(binary);
auto reduced_binary =
ReductionPass(env,
spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>())
.TryApplyReduction(binary);
CheckEqual(env, expected, reduced_binary);
}
}

View File

@@ -16,7 +16,9 @@
#include "source/opt/build_module.h"
#include "source/reduce/reduction_opportunity.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
#include "source/reduce/reduction_pass.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
#include "source/util/make_unique.h"
namespace spvtools {
namespace reduce {
@@ -73,9 +75,8 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, original, kReduceAssembleOption);
const auto pass =
TestSubclass<RemoveUnreferencedInstructionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(4, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
ops[0]->TryToApply();
@@ -132,7 +133,9 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, ApplyReduction) {
SpirvTools t(env);
ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
auto pass = TestSubclass<RemoveUnreferencedInstructionReductionPass>(env);
ReductionPass pass(
env, spvtools::MakeUnique<
RemoveUnreferencedInstructionReductionOpportunityFinder>());
{
// Attempt 1 should remove everything removable.

View File

@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/reduce/structured_loop_to_selection_reduction_pass.h"
#include "reduce_test_util.h"
#include "source/opt/build_module.h"
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
namespace spvtools {
namespace reduce {
@@ -62,8 +62,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader1) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -208,8 +208,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader2) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(4, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -677,8 +677,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader3) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(0, ops.size());
}
@@ -755,8 +755,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader4) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// Initially there are two opportunities.
ASSERT_EQ(2, ops.size());
@@ -878,8 +878,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak1) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -953,8 +953,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak2) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -1021,8 +1021,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, UnconditionalBreak) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -1221,8 +1221,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, Complex) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -1688,8 +1688,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, ComplexOptimized) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2005,8 +2005,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, DominanceIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2153,8 +2153,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, AccessChainIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2310,8 +2310,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, DominanceAndPhiIssue) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2418,8 +2418,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, OpLineBeforeOpPhi) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2508,8 +2508,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
@@ -2552,8 +2552,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// There should be no opportunities.
ASSERT_EQ(0, ops.size());
@@ -2592,8 +2592,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, ContinueTargetIsSwitchTarget) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2667,8 +2667,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2730,8 +2730,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LoopBranchesStraightToMerge) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2787,8 +2787,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2871,8 +2871,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, MultipleAccessChains) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -2967,8 +2967,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -3086,8 +3086,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
const auto ops = pass.WrapGetAvailableOpportunities(context.get());
const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(2, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());
@@ -3206,8 +3206,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
auto ops = pass.WrapGetAvailableOpportunities(context.get());
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// We cannot transform the inner loop due to its header jumping straight to
// the outer loop merge (the inner loop's merge does not post-dominate its
@@ -3251,7 +3251,8 @@ TEST(StructuredLoopToSelectionReductionPassTest,
CheckEqual(env, after_op_0, context.get());
// Now look again for more opportunities.
ops = pass.WrapGetAvailableOpportunities(context.get());
ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
// What was the inner loop should now be transformable, as the jump to the
// outer loop's merge has been redirected.
@@ -3418,8 +3419,8 @@ TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
const auto pass = TestSubclass<StructuredLoopToSelectionReductionPass>(env);
auto ops = pass.WrapGetAvailableOpportunities(context.get());
auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
.GetAvailableOpportunities(context.get());
ASSERT_EQ(1, ops.size());
ASSERT_TRUE(ops[0]->PreconditionHolds());

View File

@@ -22,24 +22,21 @@ namespace spvtools {
namespace reduce {
namespace {
// A dumb reduction pass that removes global values regardless of whether they
// are referenced. This is very likely to make the resulting module invalid. We
// use this to test the reducer's behavior in the scenario where a bad reduction
// pass leads to an invalid module.
class BlindlyRemoveGlobalValuesPass : public ReductionPass {
// A dumb reduction opportunity finder that finds opportunities to remove global
// values regardless of whether they are referenced. This is very likely to make
// the resulting module invalid. We use this to test the reducer's behavior in
// the scenario where a bad reduction pass leads to an invalid module.
class BlindlyRemoveGlobalValuesReductionOpportunityFinder
: public ReductionOpportunityFinder {
public:
// Creates the reduction pass in the context of the given target environment
// |target_env|
explicit BlindlyRemoveGlobalValuesPass(const spv_target_env target_env)
: ReductionPass(target_env) {}
BlindlyRemoveGlobalValuesReductionOpportunityFinder() = default;
~BlindlyRemoveGlobalValuesPass() override = default;
~BlindlyRemoveGlobalValuesReductionOpportunityFinder() override = default;
// The name of this pass.
std::string GetName() const final { return "BlindlyRemoveGlobalValuesPass"; };
protected:
// Adds opportunities to remove all global values. Assuming they are all
// Finds opportunities to remove all global values. Assuming they are all
// referenced (directly or indirectly) from elsewhere in the module, each such
// opportunity will make the module invalid.
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
@@ -153,7 +150,8 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
reducer.SetInterestingnessFunction(
[](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
reducer.AddReductionPass(MakeUnique<BlindlyRemoveGlobalValuesPass>(env));
reducer.AddReductionPass(
MakeUnique<BlindlyRemoveGlobalValuesReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);
@@ -357,7 +355,8 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
reducer.SetInterestingnessFunction(
[](const std::vector<uint32_t>&, uint32_t) -> bool { return true; });
reducer.AddReductionPass(MakeUnique<BlindlyRemoveGlobalValuesPass>(env));
reducer.AddReductionPass(
MakeUnique<BlindlyRemoveGlobalValuesReductionOpportunityFinder>());
std::vector<uint32_t> binary_in;
SpirvTools t(env);

View File

@@ -24,6 +24,7 @@
#include <vector>
#include "gmock/gmock.h"
#include "source/spirv_target_env.h"
#include "test/unit_spirv.h"
#include "test/val/val_fixtures.h"
@@ -53,8 +54,13 @@ using ValidateBuiltIns = spvtest::ValidateBase<bool>;
using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult =
spvtest::ValidateBase<std::tuple<const char*, const char*, const char*,
const char*, TestResult>>;
using ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult =
spvtest::ValidateBase<std::tuple<const char*, const char*, const char*,
const char*, TestResult>>;
using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
using ValidateWebGPUCombineBuiltInArrayedVariable = spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
struct EntryPoint {
std::string name;
@@ -124,6 +130,13 @@ OpCapability SampleRateShading
)";
}
std::string GetWebGPUShaderCapabilities() {
return R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
)";
}
std::string GetDefaultShaderTypes() {
return R"(
%void = OpTypeVoid
@@ -201,6 +214,55 @@ std::string GetDefaultShaderTypes() {
)";
}
std::string GetWebGPUShaderTypes() {
return R"(
%void = OpTypeVoid
%func = OpTypeFunction %void
%bool = OpTypeBool
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%f32vec2 = OpTypeVector %f32 2
%f32vec3 = OpTypeVector %f32 3
%f32vec4 = OpTypeVector %f32 4
%u32vec2 = OpTypeVector %u32 2
%u32vec3 = OpTypeVector %u32 3
%u32vec4 = OpTypeVector %u32 4
%f32_0 = OpConstant %f32 0
%f32_1 = OpConstant %f32 1
%f32_2 = OpConstant %f32 2
%f32_3 = OpConstant %f32 3
%f32_4 = OpConstant %f32 4
%f32_h = OpConstant %f32 0.5
%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
%u32_0 = OpConstant %u32 0
%u32_1 = OpConstant %u32 1
%u32_2 = OpConstant %u32 2
%u32_3 = OpConstant %u32 3
%u32_4 = OpConstant %u32 4
%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
%u32arr2 = OpTypeArray %u32 %u32_2
%u32arr3 = OpTypeArray %u32 %u32_3
%u32arr4 = OpTypeArray %u32 %u32_4
%f32arr2 = OpTypeArray %f32 %u32_2
%f32arr3 = OpTypeArray %f32 %u32_3
%f32arr4 = OpTypeArray %f32 %u32_4
%f32vec3arr3 = OpTypeArray %f32vec3 %u32_3
%f32vec4arr3 = OpTypeArray %f32vec4 %u32_3
)";
}
CodeGenerator GetDefaultShaderCodeGenerator() {
CodeGenerator generator;
generator.capabilities_ = GetDefaultShaderCapabilities();
@@ -209,24 +271,47 @@ CodeGenerator GetDefaultShaderCodeGenerator() {
return generator;
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator GetWebGPUShaderCodeGenerator() {
CodeGenerator generator;
generator.capabilities_ = GetWebGPUShaderCapabilities();
generator.memory_model_ = "OpMemoryModel Logical VulkanKHR\n";
generator.extensions_ = "OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
generator.types_ = GetWebGPUShaderTypes();
return generator;
}
bool InitializerRequired(spv_target_env env, const char* const storage_class) {
return spvIsWebGPUEnv(env) && (strncmp(storage_class, "Output", 6) == 0 ||
strncmp(storage_class, "Private", 7) == 0 ||
strncmp(storage_class, "Function", 8) == 0);
}
CodeGenerator GetInMainCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const data_type) {
CodeGenerator generator = spvIsWebGPUEnv(env)
? GetWebGPUShaderCodeGenerator()
: GetDefaultShaderCodeGenerator();
CodeGenerator generator = GetDefaultShaderCodeGenerator();
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
std::ostringstream after_types;
after_types << "%built_in_type = OpTypeStruct " << data_type << "\n";
if (InitializerRequired(env, storage_class)) {
after_types << "%built_in_null = OpConstantNull %built_in_type\n";
}
after_types << "%built_in_ptr = OpTypePointer " << storage_class
<< " %built_in_type\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class
<< "\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class;
if (InitializerRequired(env, storage_class)) {
after_types << " %built_in_null";
}
after_types << "\n";
after_types << "%data_ptr = OpTypePointer " << storage_class << " "
<< data_type << "\n";
generator.after_types_ = after_types.str();
@@ -265,6 +350,19 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) {
)";
generator.entry_points_.push_back(std::move(entry_point));
return generator;
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInMainCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
@@ -276,24 +374,52 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) {
}
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) {
TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InMain) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetDefaultShaderCodeGenerator();
CodeGenerator generator = GetInMainCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_WEBGPU_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
CodeGenerator GetInFunctionCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const data_type) {
CodeGenerator generator = spvIsWebGPUEnv(env)
? GetWebGPUShaderCodeGenerator()
: GetDefaultShaderCodeGenerator();
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
std::ostringstream after_types;
after_types << "%built_in_type = OpTypeStruct " << data_type << "\n";
if (InitializerRequired(env, storage_class)) {
after_types << "%built_in_null = OpConstantNull %built_in_type\n";
}
after_types << "%built_in_ptr = OpTypePointer " << storage_class
<< " %built_in_type\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class
<< "\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class;
if (InitializerRequired(env, storage_class)) {
after_types << " %built_in_null";
}
after_types << "\n";
after_types << "%data_ptr = OpTypePointer " << storage_class << " "
<< data_type << "\n";
generator.after_types_ = after_types.str();
@@ -331,15 +457,35 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) {
%val2 = OpFunctionCall %void %foo
)";
generator.add_at_the_end_ = R"(
std::string function_body = R"(
%foo = OpFunction %void None %func
%foo_entry = OpLabel
%ptr = OpAccessChain %data_ptr %built_in_var %u32_0
OpReturn
OpFunctionEnd
)";
if (spvIsWebGPUEnv(env)) {
generator.after_types_ += function_body;
} else {
generator.add_at_the_end_ = function_body;
}
generator.entry_points_.push_back(std::move(entry_point));
return generator;
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInFunctionCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
@@ -351,23 +497,51 @@ OpFunctionEnd
}
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InFunction) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetDefaultShaderCodeGenerator();
CodeGenerator generator = GetInFunctionCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_WEBGPU_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
CodeGenerator GetVariableCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const data_type) {
CodeGenerator generator = spvIsWebGPUEnv(env)
? GetWebGPUShaderCodeGenerator()
: GetDefaultShaderCodeGenerator();
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
std::ostringstream after_types;
if (InitializerRequired(env, storage_class)) {
after_types << "%built_in_null = OpConstantNull " << data_type << "\n";
}
after_types << "%built_in_ptr = OpTypePointer " << storage_class << " "
<< data_type << "\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class
<< "\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class;
if (InitializerRequired(env, storage_class)) {
after_types << " %built_in_null";
}
after_types << "\n";
generator.after_types_ = after_types.str();
EntryPoint entry_point;
@@ -379,7 +553,7 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
}
// Any kind of reference would do.
entry_point.body = R"(
%val = OpBitcast %u64 %built_in_var
%val = OpBitcast %u32 %built_in_var
)";
std::ostringstream execution_modes;
@@ -405,6 +579,19 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
generator.entry_points_.push_back(std::move(entry_point));
return generator;
}
TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetVariableCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
@@ -416,6 +603,27 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
}
}
TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetVariableCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_WEBGPU_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
INSTANTIATE_TEST_CASE_P(
ClipAndCullDistanceOutputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -1023,6 +1231,21 @@ INSTANTIATE_TEST_CASE_P(
"TessellationEvaluation"),
Values("Output"), Values("%f32vec4"), Values(TestResult())), );
INSTANTIATE_TEST_CASE_P(
PositionOutputSuccess,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("Position"), Values("Vertex"), Values("Output"),
Values("%f32vec4"), Values(TestResult())), );
INSTANTIATE_TEST_CASE_P(
PositionOutputFailure,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("Position"), Values("Fragment", "GLCompute"),
Values("Output"), Values("%f32vec4"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"WebGPU spec allows BuiltIn Position to be used "
"only with the Vertex execution model."))), );
INSTANTIATE_TEST_CASE_P(
PositionInputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -1030,6 +1253,16 @@ INSTANTIATE_TEST_CASE_P(
Values("Geometry", "TessellationControl", "TessellationEvaluation"),
Values("Input"), Values("%f32vec4"), Values(TestResult())), );
INSTANTIATE_TEST_CASE_P(
PositionInputFailure,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(
Values("Position"), Values("Vertex", "Fragment", "GLCompute"),
Values("Input"), Values("%f32vec4"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"WebGPU spec allows BuiltIn Position to be only used "
"for variables with Output storage class"))), );
INSTANTIATE_TEST_CASE_P(
PositionVertexInput,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -1061,6 +1294,15 @@ INSTANTIATE_TEST_CASE_P(
"needs to be a 4-component 32-bit float vector",
"is not a float vector"))), );
INSTANTIATE_TEST_CASE_P(
PositionNotFloatVector,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(
Values("Position"), Values("Vertex"), Values("Output"),
Values("%f32arr4", "%u32vec4"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector"))), );
INSTANTIATE_TEST_CASE_P(
PositionNotFloatVec4,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -1070,6 +1312,15 @@ INSTANTIATE_TEST_CASE_P(
"needs to be a 4-component 32-bit float vector",
"has 3 components"))), );
INSTANTIATE_TEST_CASE_P(
PositionNotFloatVec4,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(
Values("Position"), Values("Vertex"), Values("Output"),
Values("%f32vec3"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector"))), );
INSTANTIATE_TEST_CASE_P(
PositionNotF32Vec4,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@@ -1549,24 +1800,32 @@ INSTANTIATE_TEST_CASE_P(
"needs to be a 32-bit int scalar",
"has bit width 64"))), );
TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator GetArrayedVariableCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const data_type) {
CodeGenerator generator = spvIsWebGPUEnv(env)
? GetWebGPUShaderCodeGenerator()
: GetDefaultShaderCodeGenerator();
CodeGenerator generator = GetDefaultShaderCodeGenerator();
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
std::ostringstream after_types;
after_types << "%built_in_array = OpTypeArray " << data_type << " %u32_3\n";
if (InitializerRequired(env, storage_class)) {
after_types << "%built_in_array_null = OpConstantNull %built_in_array\n";
}
after_types << "%built_in_ptr = OpTypePointer " << storage_class
<< " %built_in_array\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class
<< "\n";
after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class;
if (InitializerRequired(env, storage_class)) {
after_types << " %built_in_array_null";
}
after_types << "\n";
generator.after_types_ = after_types.str();
EntryPoint entry_point;
@@ -1575,7 +1834,7 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
entry_point.interfaces = "%built_in_var";
// Any kind of reference would do.
entry_point.body = R"(
%val = OpBitcast %u64 %built_in_var
%val = OpBitcast %u32 %built_in_var
)";
std::ostringstream execution_modes;
@@ -1601,6 +1860,19 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
generator.entry_points_.push_back(std::move(entry_point));
return generator;
}
TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetArrayedVariableCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
@@ -1612,6 +1884,27 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) {
}
}
TEST_P(ValidateWebGPUCombineBuiltInArrayedVariable, Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetArrayedVariableCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_WEBGPU_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
INSTANTIATE_TEST_CASE_P(PointSizeArrayedF32TessControl,
ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("PointSize"),
@@ -1652,7 +1945,15 @@ INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_CASE_P(
PositionArrayedF32Vec4Vertex, ValidateVulkanCombineBuiltInArrayedVariable,
Combine(Values("Position"), Values("Vertex"), Values("Output"),
Values("%f32"),
Values("%f32vec4"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector",
"is not a float vector"))), );
INSTANTIATE_TEST_CASE_P(
PositionArrayedF32Vec4Vertex, ValidateWebGPUCombineBuiltInArrayedVariable,
Combine(Values("Position"), Values("Vertex"), Values("Output"),
Values("%f32vec4"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 4-component 32-bit float vector",
"is not a float vector"))), );

View File

@@ -493,13 +493,10 @@ TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) {
str += "OpFunctionEnd\n";
CompileSuccessfully(str);
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
EXPECT_THAT(
getDiagnosticString(),
MatchesRegex("Block\\(s\\) \\{11\\[%11\\]\\} are referenced but not "
"defined in function .\\[%Main\\]\n %Main = OpFunction "
"%void None %10\n"))
<< str;
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("'Target Label' operands for OpBranch must "
"be the ID of an OpLabel instruction"));
}
TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) {
@@ -2060,6 +2057,57 @@ TEST_F(ValidateCFG, KernelWithPhiPtr) {
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateCFG, SwitchTargetMustBeLabel) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "foo"
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%void = OpTypeVoid
%5 = OpTypeFunction %void
%1 = OpFunction %void None %5
%6 = OpLabel
%7 = OpCopyObject %uint %uint_0
OpSelectionMerge %8 None
OpSwitch %uint_0 %8 0 %7
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("'Target Label' operands for OpSwitch must "
"be IDs of an OpLabel instruction"));
}
TEST_F(ValidateCFG, BranchTargetMustBeLabel) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "foo"
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%void = OpTypeVoid
%5 = OpTypeFunction %void
%1 = OpFunction %void None %5
%2 = OpLabel
%7 = OpCopyObject %uint %uint_0
OpBranch %7
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("'Target Label' operands for OpBranch must "
"be the ID of an OpLabel instruction"));
}
/// TODO(umar): Nested CFG constructs
} // namespace

View File

@@ -934,6 +934,23 @@ TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) {
"OpTypeStruct %_runtimearr_uint %uint\n"));
}
TEST_F(ValidateData, invalid_forward_reference_in_array) {
std::string str = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_arr_3_uint_1 = OpTypeArray %_arr_3_uint_1 %uint_1
)";
CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3);
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Forward reference operands in an OpTypeArray must "
"first be declared using OpTypeForwardPointer."));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -3285,7 +3285,7 @@ TEST_F(ValidateDecorations,
getDiagnosticString(),
HasSubstr("Structure id 6 decorated as Block for variable in Uniform "
"storage class must follow standard uniform buffer layout "
"rules: member 2 at offset 24 is not aligned to 16"));
"rules: member 2 at offset 152 is not aligned to 16"));
}
TEST_F(ValidateDecorations,
@@ -3418,7 +3418,8 @@ TEST_F(ValidateDecorations, BlockUniformBufferLayoutIncorrectArrayStrideBad) {
getDiagnosticString(),
HasSubstr(
"Structure id 6 decorated as Block for variable in Uniform storage "
"class must follow standard uniform buffer layout rules: member 4 is "
"class must follow standard uniform buffer layout rules: member 4 "
"contains "
"an array with stride 49 not satisfying alignment to 16"));
}
@@ -5302,6 +5303,194 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}
TEST_F(ValidateDecorations, InvalidStraddle) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %inner_struct 0 Offset 0
OpMemberDecorate %inner_struct 1 Offset 4
OpDecorate %outer_struct Block
OpMemberDecorate %outer_struct 0 Offset 0
OpMemberDecorate %outer_struct 1 Offset 8
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%float = OpTypeFloat 32
%float2 = OpTypeVector %float 2
%inner_struct = OpTypeStruct %float %float2
%outer_struct = OpTypeStruct %float2 %inner_struct
%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct
%var = OpVariable %ptr_ssbo_outer StorageBuffer
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure id 2 decorated as Block for variable in "
"StorageBuffer storage class must follow relaxed "
"storage buffer layout rules: member 1 is an "
"improperly straddling vector at offset 12"));
}
TEST_F(ValidateDecorations, DescriptorArray) {
const std::string spirv = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%float2 = OpTypeVector %float 2
%struct = OpTypeStruct %float %float2
%struct_array = OpTypeArray %struct %int_2
%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
%var = OpVariable %ptr_ssbo_array StorageBuffer
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure id 2 decorated as Block for variable in "
"StorageBuffer storage class must follow standard "
"storage buffer layout rules: member 1 at offset 1 is "
"not aligned to 8"));
}
TEST_F(ValidateDecorations, DescriptorRuntimeArray) {
const std::string spirv = R"(
OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 1 Offset 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%float2 = OpTypeVector %float 2
%struct = OpTypeStruct %float %float2
%struct_array = OpTypeRuntimeArray %struct
%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
%var = OpVariable %ptr_ssbo_array StorageBuffer
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure id 2 decorated as Block for variable in "
"StorageBuffer storage class must follow standard "
"storage buffer layout rules: member 1 at offset 1 is "
"not aligned to 8"));
}
TEST_F(ValidateDecorations, MultiDimensionalArray) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %array_4 ArrayStride 4
OpDecorate %array_3 ArrayStride 48
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%array_4 = OpTypeArray %int %int_4
%array_3 = OpTypeArray %array_4 %int_3
%struct = OpTypeStruct %array_3
%ptr_struct = OpTypePointer Uniform %struct
%var = OpVariable %ptr_struct Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure id 2 decorated as Block for variable in "
"Uniform storage class must follow standard uniform "
"buffer layout rules: member 0 contains an array with "
"stride 4 not satisfying alignment to 16"));
}
TEST_F(ValidateDecorations, ImproperStraddleInArray) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %array ArrayStride 24
OpMemberDecorate %inner 0 Offset 0
OpMemberDecorate %inner 1 Offset 4
OpMemberDecorate %inner 2 Offset 12
OpMemberDecorate %inner 3 Offset 16
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%int2 = OpTypeVector %int 2
%inner = OpTypeStruct %int %int2 %int %int
%array = OpTypeArray %inner %int_2
%struct = OpTypeStruct %array
%ptr_struct = OpTypePointer StorageBuffer %struct
%var = OpVariable %ptr_struct StorageBuffer
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Structure id 4 decorated as Block for variable in "
"StorageBuffer storage class must follow relaxed "
"storage buffer layout rules: member 1 is an "
"improperly straddling vector at offset 28"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -449,12 +449,11 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpVariable, <id> '5[%5]', has a disallowed initializer & storage "
"class combination.\nFrom WebGPU execution environment spec:\n"
"Variable declarations that include initializers must have one of "
"the following storage classes: Output, Private, or Function\n"
" %5 = OpVariable %_ptr_Uniform_float Uniform %float_1\n"));
HasSubstr("OpVariable, <id> '5[%5]', has a disallowed initializer & "
"storage class combination.\nFrom WebGPU spec:\nVariable "
"declarations that include initializers must have one of the "
"following storage classes: Output, Private, or Function\n %5 "
"= OpVariable %_ptr_Uniform_float Uniform %float_1\n"));
}
TEST_F(ValidateMemory, WebGPUOutputStorageClassWithoutInitializerBad) {
@@ -628,12 +627,11 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"OpVariable, <id> '5[%5]', has a disallowed initializer & storage "
"class combination.\nFrom Vulkan spec, Appendix A:\n"
"Variable declarations that include initializers must have one of "
"the following storage classes: Output, Private, or Function\n "
"%5 = OpVariable %_ptr_Input_float Input %float_1\n"));
HasSubstr("OpVariable, <id> '5[%5]', has a disallowed initializer & "
"storage class combination.\nFrom Vulkan spec:\nVariable "
"declarations that include initializers must have one of the "
"following storage classes: Output, Private, or Function\n %5 "
"= OpVariable %_ptr_Input_float Input %float_1\n"));
}
TEST_F(ValidateMemory, ArrayLenCorrectResultType) {
@@ -2179,7 +2177,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_2 = "
"valid in Vulkan environments.\n %_runtimearr__runtimearr_2 = "
"OpTypeRuntimeArray %_runtimearr_2\n"));
}
@@ -2210,7 +2208,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not "
"valid in WebGPU environment.\n %_runtimearr__runtimearr_2 = "
"valid in WebGPU environments.\n %_runtimearr__runtimearr_2 = "
"OpTypeRuntimeArray %_runtimearr_2\n"));
}
@@ -2242,7 +2240,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '4[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
@@ -2304,7 +2302,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '5[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
@@ -2340,7 +2338,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '5[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
@@ -2370,7 +2368,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_4_uint_1 = "
"valid in Vulkan environments.\n %_arr__runtimearr_4_uint_1 = "
"OpTypeArray %_runtimearr_4 %uint_1\n"));
}
@@ -2402,7 +2400,7 @@ OpFunctionEnd
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
"valid in WebGPU environment.\n %_arr__runtimearr_4_uint_1 = "
"valid in WebGPU environments.\n %_arr__runtimearr_4_uint_1 = "
"OpTypeArray %_runtimearr_4 %uint_1\n"));
}
@@ -2436,7 +2434,7 @@ OpFunctionEnd
getDiagnosticString(),
HasSubstr(
"OpTypeRuntimeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_runtimearr__runtimearr_uint = "
"valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = "
"OpTypeRuntimeArray %_runtimearr_uint\n"));
}
@@ -2468,9 +2466,10 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
HasSubstr(
"OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environments.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
}
TEST_F(ValidateMemory,
@@ -2504,9 +2503,10 @@ OpFunctionEnd
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environment.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
HasSubstr(
"OpTypeArray Element Type <id> '6[%_runtimearr_uint]' is not "
"valid in Vulkan environments.\n %_arr__runtimearr_uint_uint_1 "
"= OpTypeArray %_runtimearr_uint %uint_1\n"));
}
TEST_F(ValidateMemory, VulkanRTAStructInsideRTAWithRuntimeDescriptorArrayGood) {

View File

@@ -20,13 +20,13 @@
#include "source/opt/build_module.h"
#include "source/opt/ir_context.h"
#include "source/opt/log.h"
#include "source/reduce/operand_to_const_reduction_pass.h"
#include "source/reduce/operand_to_dominating_id_reduction_pass.h"
#include "source/reduce/operand_to_undef_reduction_pass.h"
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
#include "source/reduce/reducer.h"
#include "source/reduce/remove_opname_instruction_reduction_pass.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_pass.h"
#include "source/reduce/structured_loop_to_selection_reduction_pass.h"
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
#include "source/spirv_reducer_options.h"
#include "source/util/make_unique.h"
#include "source/util/string_utils.h"
@@ -77,7 +77,26 @@ USAGE: %s [options] <input> <interestingness-test>
The SPIR-V binary is read from <input>.
Whether a binary is interesting is determined by <interestingness-test>, which
is typically a script.
should be the path to a script.
* The script must be executable.
* The script should take the path to a SPIR-V binary file (.spv) as its single
argument, and exit with code 0 if and only if the binary file is
interesting.
* Example: an interestingness test for reducing a SPIR-V binary file that
causes tool "foo" to exit with error code 1 and print "Fatal error: bar" to
standard error should:
- invoke "foo" on the binary passed as the script argument;
- capture the return code and standard error from "bar";
- exit with code 0 if and only if the return code of "foo" was 1 and the
standard error from "bar" contained "Fatal error: bar".
* The reducer does not place a time limit on how long the interestingness test
takes to run, so it is advisable to use per-command timeouts inside the
script when invoking SPIR-V-processing tools (such as "foo" in the above
example).
NOTE: The reducer is a work in progress.
@@ -85,8 +104,8 @@ Options (in lexicographical order):
-h, --help
Print this help.
--step-limit
32-bit unsigned integer specifying maximum number of
steps the reducer will take before giving up.
32-bit unsigned integer specifying maximum number of steps the
reducer will take before giving up.
--version
Display reducer version information.
)",
@@ -207,18 +226,20 @@ int main(int argc, const char** argv) {
});
reducer.AddReductionPass(
spvtools::MakeUnique<RemoveOpNameInstructionReductionPass>(target_env));
spvtools::MakeUnique<
RemoveOpNameInstructionReductionOpportunityFinder>());
reducer.AddReductionPass(
spvtools::MakeUnique<OperandToUndefReductionPass>(target_env));
spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
reducer.AddReductionPass(
spvtools::MakeUnique<OperandToConstReductionPass>(target_env));
spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
reducer.AddReductionPass(
spvtools::MakeUnique<OperandToDominatingIdReductionPass>(target_env));
spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
reducer.AddReductionPass(
spvtools::MakeUnique<RemoveUnreferencedInstructionReductionPass>(
target_env));
spvtools::MakeUnique<
RemoveUnreferencedInstructionReductionOpportunityFinder>());
reducer.AddReductionPass(
spvtools::MakeUnique<StructuredLoopToSelectionReductionPass>(target_env));
spvtools::MakeUnique<
StructuredLoopToSelectionReductionOpportunityFinder>());
reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);

View File

@@ -26,6 +26,8 @@ project "spirv-opt"
files {
path.join(SPIRV_TOOLS, "source/opt/**.cpp"),
path.join(SPIRV_TOOLS, "source/opt/**.h"),
path.join(SPIRV_TOOLS, "source/reduce/**.cpp"),
path.join(SPIRV_TOOLS, "source/reduce/**.h"),
-- libspirv
path.join(SPIRV_TOOLS, "source/assembly_grammar.cpp"),