mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
1
3rdparty/spirv-tools/Android.mk
vendored
1
3rdparty/spirv-tools/Android.mk
vendored
@@ -156,6 +156,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/scalar_replacement_pass.cpp \
|
||||
source/opt/set_spec_constant_default_value_pass.cpp \
|
||||
source/opt/simplification_pass.cpp \
|
||||
source/opt/split_invalid_unreachable_pass.cpp \
|
||||
source/opt/ssa_rewrite_pass.cpp \
|
||||
source/opt/strength_reduction_pass.cpp \
|
||||
source/opt/strip_atomic_counter_memory_pass.cpp \
|
||||
|
||||
2
3rdparty/spirv-tools/BUILD.gn
vendored
2
3rdparty/spirv-tools/BUILD.gn
vendored
@@ -616,6 +616,8 @@ static_library("spvtools_opt") {
|
||||
"source/opt/set_spec_constant_default_value_pass.h",
|
||||
"source/opt/simplification_pass.cpp",
|
||||
"source/opt/simplification_pass.h",
|
||||
"source/opt/split_invalid_unreachable_pass.cpp",
|
||||
"source/opt/split_invalid_unreachable_pass.h",
|
||||
"source/opt/ssa_rewrite_pass.cpp",
|
||||
"source/opt/ssa_rewrite_pass.h",
|
||||
"source/opt/strength_reduction_pass.cpp",
|
||||
|
||||
22
3rdparty/spirv-tools/CHANGES
vendored
22
3rdparty/spirv-tools/CHANGES
vendored
@@ -1,13 +1,22 @@
|
||||
Revision history for SPIRV-Tools
|
||||
|
||||
v2019.3-dev 2019-04-03
|
||||
v2019.4-dev 2019-05-15
|
||||
- Start v2019.4-dev
|
||||
|
||||
v2019.3 2019-05-14
|
||||
- General:
|
||||
- Updated Python scripts to work for both Python 2 and Python 3.
|
||||
- Require Python 3 since Python 2 will out of service soon.
|
||||
- Add a continuous test that does memory checks using the address sanitizer.
|
||||
- Fix the build files so the SPIRV_USE_SANITIZER=address build works.
|
||||
- Packaging top of tree build artifacts again.
|
||||
- Added support for SPIR-V 1.4. (#2550)
|
||||
- Optimizer
|
||||
- Remove duplicates from list of interface IDs in OpEntryPoint instruction (#2449)
|
||||
- Bindless Validation: Descriptor Initialization Check (#2419)
|
||||
- Add option to validate after each pass (#2462)
|
||||
- Add legalization pass to fix mismatched pointer (#2430, #2535)
|
||||
- Add error messages when the input contains unknown instructions. (#2487)
|
||||
- Add pass to convert from WebGPU Spir-V to Vulkan Spir-V and back. (#2495)
|
||||
Fixes:
|
||||
- #2412: Dead memeber elimination should not change input and output variables.
|
||||
- #2405: Fix OpDot folding of half float vectors.
|
||||
@@ -18,6 +27,8 @@ v2019.3-dev 2019-04-03
|
||||
- #2456: Handle dead infinite loops in DCE.
|
||||
- #2458: Handle variable pointer in some optimizations.
|
||||
- #2452: Fix dead branch elimination to handle unreachable blocks better.
|
||||
- #2528: Fix undefined bit shift in sroa.
|
||||
- #2539: Change implementation of post order CFG traversal.
|
||||
- Validator
|
||||
- Add validation of storage classes for WebGPU (#2446)
|
||||
- Add validation for ExecutionMode in WebGPU (#2443)
|
||||
@@ -32,6 +43,12 @@ v2019.3-dev 2019-04-03
|
||||
- Handle function decls in Structured CFG analysis (#2474)
|
||||
- Validate that OpUnreacahble is not statically reachable (#2473)
|
||||
- Add pass to generate needed initializers for WebGPU (#2481)
|
||||
- Allow images without format for OpenCL. (#2470)
|
||||
- Remove unreachable block validation (#2525)
|
||||
- Reduce runtime of array layout checks (#2534)
|
||||
- Add validation specific to OpExecutionModeId (#2536)
|
||||
- Validate sign of int types. (#2549)
|
||||
- VK_KHR_uniform_buffer_standard_layout validation (#2562)
|
||||
Fixes:
|
||||
- #2439: Add missing DepthGreater case to Fragment only check.
|
||||
- #2168: Disallow BufferBlock on StorageBuffer variables for Vulkan.
|
||||
@@ -42,6 +59,7 @@ v2019.3-dev 2019-04-03
|
||||
- Allows passing options to the validator. (#2401)
|
||||
- Improve reducer algorithm and other changes (#2472)
|
||||
- Add Pass to remove selections (#2485)
|
||||
- Add passes to simplify branches (#2507)
|
||||
Fixes:
|
||||
- #2478: fix loop to selection pass for loops with combined header/continue block
|
||||
|
||||
|
||||
14
3rdparty/spirv-tools/README.md
vendored
14
3rdparty/spirv-tools/README.md
vendored
@@ -1,10 +1,5 @@
|
||||
# SPIR-V Tools
|
||||
|
||||
[](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
|
||||
<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>
|
||||
<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>
|
||||
<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>
|
||||
|
||||
## Overview
|
||||
|
||||
The SPIR-V Tools project provides an API and commands for processing SPIR-V
|
||||
@@ -24,6 +19,15 @@ SPIR-V is defined by the Khronos Group Inc.
|
||||
See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification,
|
||||
headers, and XML registry.
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master)
|
||||
<img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
|
||||
<img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
|
||||
<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html)
|
||||
|
||||
[More downloads](downloads.md)
|
||||
|
||||
## Versioning SPIRV-Tools
|
||||
|
||||
See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
|
||||
|
||||
14
3rdparty/spirv-tools/downloads.md
vendored
Normal file
14
3rdparty/spirv-tools/downloads.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Downloads
|
||||
Download the latest builds.
|
||||
|
||||
## Release
|
||||
| Windows | Linux | MacOS |
|
||||
| --- | --- | --- |
|
||||
| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) |
|
||||
| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | |
|
||||
|
||||
## Debug
|
||||
| Windows | Linux | MacOS |
|
||||
| --- | --- | --- |
|
||||
| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) |
|
||||
| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | |
|
||||
@@ -1 +1 @@
|
||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-159-g5fc5303e"
|
||||
"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-6-g47741f0"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,8 @@ const char* ExtensionToString(Extension extension) {
|
||||
return "SPV_INTEL_device_side_avc_motion_estimation";
|
||||
case Extension::kSPV_INTEL_media_block_io:
|
||||
return "SPV_INTEL_media_block_io";
|
||||
case Extension::kSPV_INTEL_shader_integer_functions2:
|
||||
return "SPV_INTEL_shader_integer_functions2";
|
||||
case Extension::kSPV_INTEL_subgroups:
|
||||
return "SPV_INTEL_subgroups";
|
||||
case Extension::kSPV_KHR_16bit_storage:
|
||||
@@ -105,8 +107,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_physical_storage_buffer", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_media_block_io", "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_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_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_device_side_avc_motion_estimation, Extension::kSPV_INTEL_media_block_io, 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_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_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_device_side_avc_motion_estimation", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "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_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_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_device_side_avc_motion_estimation, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, 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_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_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(
|
||||
@@ -358,6 +360,8 @@ const char* CapabilityToString(SpvCapability capability) {
|
||||
return "SubgroupImageBlockIOINTEL";
|
||||
case SpvCapabilitySubgroupImageMediaBlockIOINTEL:
|
||||
return "SubgroupImageMediaBlockIOINTEL";
|
||||
case SpvCapabilityIntegerFunctions2INTEL:
|
||||
return "IntegerFunctions2INTEL";
|
||||
case SpvCapabilitySubgroupAvcMotionEstimationINTEL:
|
||||
return "SubgroupAvcMotionEstimationINTEL";
|
||||
case SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL:
|
||||
|
||||
@@ -18,6 +18,7 @@ kSPV_GOOGLE_decorate_string,
|
||||
kSPV_GOOGLE_hlsl_functionality1,
|
||||
kSPV_INTEL_device_side_avc_motion_estimation,
|
||||
kSPV_INTEL_media_block_io,
|
||||
kSPV_INTEL_shader_integer_functions2,
|
||||
kSPV_INTEL_subgroups,
|
||||
kSPV_KHR_16bit_storage,
|
||||
kSPV_KHR_8bit_storage,
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
{18, "Wine", "VKD3D Shader Compiler", "Wine VKD3D Shader Compiler"},
|
||||
{19, "Clay", "Clay Shader Compiler", "Clay Clay Shader Compiler"},
|
||||
{20, "W3C WebGPU Group", "WHLSL Shader Translator", "W3C WebGPU Group WHLSL Shader Translator"},
|
||||
{21, "Google", "Clspv", "Google Clspv"},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -427,6 +427,8 @@ typedef enum {
|
||||
SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision.
|
||||
SPV_ENV_WEBGPU_0, // Work in progress WebGPU 1.0.
|
||||
SPV_ENV_UNIVERSAL_1_4, // SPIR-V 1.4 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_1_SPIRV_1_4, // Vulkan 1.1 with SPIR-V 1.4 binary.
|
||||
} spv_target_env;
|
||||
|
||||
// SPIR-V Validator can be parameterized with the following Universal Limits.
|
||||
@@ -492,6 +494,20 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxStoreStruct(
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether or not the validator should relax the rules because it is
|
||||
// expected that the optimizations will make the code legal.
|
||||
//
|
||||
// When relaxed, it will allow the following:
|
||||
// 1) It will allow relaxed logical pointers. Setting this option will also
|
||||
// set that option.
|
||||
// 2) Pointers that are pass as parameters to function calls do not have to
|
||||
// match the storage class of the formal parameter.
|
||||
// 3) Pointers that are actaul parameters on function calls do not have to point
|
||||
// to the same type pointed as the formal parameter. The types just need to
|
||||
// logically match.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBeforeHlslLegalization(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether the validator should use "relaxed" block layout rules.
|
||||
// Relaxed layout rules are described by Vulkan extension
|
||||
// VK_KHR_relaxed_block_layout, and they affect uniform blocks, storage blocks,
|
||||
@@ -502,6 +518,11 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer(
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether the validator should use standard block layout rules for
|
||||
// uniform blocks.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether the validator should use "scalar" block layout rules.
|
||||
// Scalar layout rules are more permissive than relaxed block layout.
|
||||
//
|
||||
|
||||
@@ -88,6 +88,12 @@ class ValidatorOptions {
|
||||
spvValidatorOptionsSetRelaxBlockLayout(options_, val);
|
||||
}
|
||||
|
||||
// Enables VK_KHR_uniform_buffer_standard_layout when validating standard
|
||||
// uniform layout. If true, disables scalar block layout rules.
|
||||
void SetUniformBufferStandardLayout(bool val) {
|
||||
spvValidatorOptionsSetUniformBufferStandardLayout(options_, val);
|
||||
}
|
||||
|
||||
// Enables VK_EXT_scalar_block_layout when validating standard
|
||||
// uniform/storage buffer/push-constant layout. If true, disables
|
||||
// relaxed block layout rules.
|
||||
@@ -110,6 +116,21 @@ class ValidatorOptions {
|
||||
spvValidatorOptionsSetRelaxLogicalPointer(options_, val);
|
||||
}
|
||||
|
||||
// Records whether or not the validator should relax the rules because it is
|
||||
// expected that the optimizations will make the code legal.
|
||||
//
|
||||
// When relaxed, it will allow the following:
|
||||
// 1) It will allow relaxed logical pointers. Setting this option will also
|
||||
// set that option.
|
||||
// 2) Pointers that are pass as parameters to function calls do not have to
|
||||
// match the storage class of the formal parameter.
|
||||
// 3) Pointers that are actaul parameters on function calls do not have to
|
||||
// point to the same type pointed as the formal parameter. The types just
|
||||
// need to logically match.
|
||||
void SetBeforeHlslLegalization(bool val) {
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(options_, val);
|
||||
}
|
||||
|
||||
private:
|
||||
spv_validator_options options_;
|
||||
};
|
||||
|
||||
@@ -772,6 +772,10 @@ Optimizer::PassToken CreateLegalizeVectorShufflePass();
|
||||
// declaration and an initial store.
|
||||
Optimizer::PassToken CreateDecomposeInitializedVariablesPass();
|
||||
|
||||
// Create a pass to attempt to split up invalid unreachable merge-blocks and
|
||||
// continue-targets to legalize for WebGPU.
|
||||
Optimizer::PassToken CreateSplitInvalidUnreachablePass();
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
|
||||
|
||||
3
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
3
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
@@ -30,6 +30,7 @@
|
||||
#include "glsl.std.450.insts.inc"
|
||||
#include "opencl.std.insts.inc"
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "spv-amd-gcn-shader.insts.inc"
|
||||
#include "spv-amd-shader-ballot.insts.inc"
|
||||
#include "spv-amd-shader-explicit-vertex-parameter.insts.inc"
|
||||
@@ -80,7 +81,9 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
|
||||
case SPV_ENV_OPENGL_4_5:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
*pExtInstTable = &kTable_1_0;
|
||||
return SPV_SUCCESS;
|
||||
default:
|
||||
|
||||
14
3rdparty/spirv-tools/source/opcode.cpp
vendored
14
3rdparty/spirv-tools/source/opcode.cpp
vendored
@@ -97,6 +97,7 @@ spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
|
||||
// preferable but the table requires sorting on the Opcode name, but it's
|
||||
// static const initialized and matches the order of the spec.
|
||||
const size_t nameLength = strlen(name);
|
||||
const auto version = spvVersionForTargetEnv(env);
|
||||
for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
|
||||
const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
|
||||
// We considers the current opcode as available as long as
|
||||
@@ -107,7 +108,7 @@ spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
|
||||
// Note that the second rule assumes the extension enabling this instruction
|
||||
// is indeed requested in the SPIR-V code; checking that should be
|
||||
// validator's work.
|
||||
if ((spvVersionForTargetEnv(env) >= entry.minVersion ||
|
||||
if (((version >= entry.minVersion && version <= entry.lastVersion) ||
|
||||
entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
|
||||
nameLength == strlen(entry.name) &&
|
||||
!strncmp(name, entry.name, nameLength)) {
|
||||
@@ -130,8 +131,8 @@ spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
|
||||
const auto beg = table->entries;
|
||||
const auto end = table->entries + table->count;
|
||||
|
||||
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
|
||||
false, false, 0, nullptr, ~0u};
|
||||
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
|
||||
false, false, 0, nullptr, ~0u, ~0u};
|
||||
|
||||
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
|
||||
return lhs.opcode < rhs.opcode;
|
||||
@@ -142,6 +143,7 @@ spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
|
||||
// which means they can have different minimal version requirements.
|
||||
// Assumes the underlying table is already sorted ascendingly according to
|
||||
// opcode value.
|
||||
const auto version = spvVersionForTargetEnv(env);
|
||||
for (auto it = std::lower_bound(beg, end, needle, comp);
|
||||
it != end && it->opcode == opcode; ++it) {
|
||||
// We considers the current opcode as available as long as
|
||||
@@ -152,7 +154,7 @@ spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
|
||||
// Note that the second rule assumes the extension enabling this instruction
|
||||
// is indeed requested in the SPIR-V code; checking that should be
|
||||
// validator's work.
|
||||
if (spvVersionForTargetEnv(env) >= it->minVersion ||
|
||||
if ((version >= it->minVersion && version <= it->lastVersion) ||
|
||||
it->numExtensions > 0u || it->numCapabilities > 0u) {
|
||||
*pEntry = it;
|
||||
return SPV_SUCCESS;
|
||||
@@ -182,8 +184,8 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
|
||||
const char* spvOpcodeString(const SpvOp opcode) {
|
||||
const auto beg = kOpcodeTableEntries;
|
||||
const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
|
||||
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
|
||||
false, false, 0, nullptr, ~0u};
|
||||
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
|
||||
false, false, 0, nullptr, ~0u, ~0u};
|
||||
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
|
||||
return lhs.opcode < rhs.opcode;
|
||||
};
|
||||
|
||||
9
3rdparty/spirv-tools/source/operand.cpp
vendored
9
3rdparty/spirv-tools/source/operand.cpp
vendored
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "source/macro.h"
|
||||
@@ -50,6 +51,7 @@ spv_result_t spvOperandTableNameLookup(spv_target_env env,
|
||||
if (!table) return SPV_ERROR_INVALID_TABLE;
|
||||
if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
|
||||
|
||||
const auto version = spvVersionForTargetEnv(env);
|
||||
for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) {
|
||||
const auto& group = table->types[typeIndex];
|
||||
if (type != group.type) continue;
|
||||
@@ -64,7 +66,7 @@ spv_result_t spvOperandTableNameLookup(spv_target_env env,
|
||||
// Note that the second rule assumes the extension enabling this operand
|
||||
// is indeed requested in the SPIR-V code; checking that should be
|
||||
// validator's work.
|
||||
if ((spvVersionForTargetEnv(env) >= entry.minVersion ||
|
||||
if (((version >= entry.minVersion && version <= entry.lastVersion) ||
|
||||
entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
|
||||
nameLength == strlen(entry.name) &&
|
||||
!strncmp(entry.name, name, nameLength)) {
|
||||
@@ -85,7 +87,7 @@ spv_result_t spvOperandTableValueLookup(spv_target_env env,
|
||||
if (!table) return SPV_ERROR_INVALID_TABLE;
|
||||
if (!pEntry) return SPV_ERROR_INVALID_POINTER;
|
||||
|
||||
spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, {}, ~0u};
|
||||
spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, {}, ~0u, ~0u};
|
||||
|
||||
auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) {
|
||||
return lhs.value < rhs.value;
|
||||
@@ -108,6 +110,7 @@ spv_result_t spvOperandTableValueLookup(spv_target_env env,
|
||||
// requirements.
|
||||
// Assumes the underlying table is already sorted ascendingly according to
|
||||
// opcode value.
|
||||
const auto version = spvVersionForTargetEnv(env);
|
||||
for (auto it = std::lower_bound(beg, end, needle, comp);
|
||||
it != end && it->value == value; ++it) {
|
||||
// We consider the current operand as available as long as
|
||||
@@ -119,7 +122,7 @@ spv_result_t spvOperandTableValueLookup(spv_target_env env,
|
||||
// Note that the second rule assumes the extension enabling this operand
|
||||
// is indeed requested in the SPIR-V code; checking that should be
|
||||
// validator's work.
|
||||
if (spvVersionForTargetEnv(env) >= it->minVersion ||
|
||||
if ((version >= it->minVersion && version <= it->lastVersion) ||
|
||||
it->numExtensions > 0u || it->numCapabilities > 0u) {
|
||||
*pEntry = it;
|
||||
return SPV_SUCCESS;
|
||||
|
||||
@@ -97,6 +97,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
scalar_replacement_pass.h
|
||||
set_spec_constant_default_value_pass.h
|
||||
simplification_pass.h
|
||||
split_invalid_unreachable_pass.h
|
||||
ssa_rewrite_pass.h
|
||||
strength_reduction_pass.h
|
||||
strip_atomic_counter_memory_pass.h
|
||||
@@ -195,6 +196,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
scalar_replacement_pass.cpp
|
||||
set_spec_constant_default_value_pass.cpp
|
||||
simplification_pass.cpp
|
||||
split_invalid_unreachable_pass.cpp
|
||||
ssa_rewrite_pass.cpp
|
||||
strength_reduction_pass.cpp
|
||||
strip_atomic_counter_memory_pass.cpp
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/opt/iterator.h"
|
||||
#include "source/opt/reflect.h"
|
||||
#include "source/spirv_constant.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -548,7 +549,26 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
}
|
||||
// Keep all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
AddToWorklist(&entry);
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// In SPIR-V 1.4 and later, entry points must list all global variables
|
||||
// used. DCE can still remove non-input/output variables and update the
|
||||
// interface list. Mark the entry point as live and inputs and outputs as
|
||||
// live, but defer decisions all other interfaces.
|
||||
live_insts_.Set(entry.unique_id());
|
||||
// The actual function is live always.
|
||||
AddToWorklist(
|
||||
get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(1u)));
|
||||
for (uint32_t i = 3; i < entry.NumInOperands(); ++i) {
|
||||
auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
|
||||
auto storage_class = var->GetSingleWordInOperand(0u);
|
||||
if (storage_class == SpvStorageClassInput ||
|
||||
storage_class == SpvStorageClassOutput) {
|
||||
AddToWorklist(var);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AddToWorklist(&entry);
|
||||
}
|
||||
}
|
||||
// Keep workgroup size.
|
||||
for (auto& anno : get_module()->annotations()) {
|
||||
@@ -780,6 +800,29 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
|
||||
}
|
||||
}
|
||||
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Remove the dead interface variables from the entry point interface list.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
std::vector<Operand> new_operands;
|
||||
for (uint32_t i = 0; i < entry.NumInOperands(); ++i) {
|
||||
if (i < 3) {
|
||||
// Execution model, function id and name are always valid.
|
||||
new_operands.push_back(entry.GetInOperand(i));
|
||||
} else {
|
||||
auto* var =
|
||||
get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
|
||||
if (!IsDead(var)) {
|
||||
new_operands.push_back(entry.GetInOperand(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new_operands.size() != entry.NumInOperands()) {
|
||||
entry.SetInOperands(std::move(new_operands));
|
||||
get_def_use_mgr()->UpdateDefUse(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,10 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule(
|
||||
const Constant* new_const, Module::inst_iterator* pos, uint32_t type_id) {
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto new_inst = CreateInstruction(new_id, new_const, type_id);
|
||||
if (!new_inst) {
|
||||
return nullptr;
|
||||
|
||||
13
3rdparty/spirv-tools/source/opt/function.cpp
vendored
13
3rdparty/spirv-tools/source/opt/function.cpp
vendored
@@ -147,6 +147,19 @@ BasicBlock* Function::InsertBasicBlockAfter(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BasicBlock* Function::InsertBasicBlockBefore(
|
||||
std::unique_ptr<BasicBlock>&& new_block, BasicBlock* position) {
|
||||
for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) {
|
||||
if (&*bb_iter == position) {
|
||||
new_block->SetParent(this);
|
||||
bb_iter = bb_iter.InsertBefore(std::move(new_block));
|
||||
return &*bb_iter;
|
||||
}
|
||||
}
|
||||
assert(false && "Could not find insertion point.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Function::IsRecursive() const {
|
||||
IRContext* ctx = blocks_.front()->GetLabel()->context();
|
||||
IRContext::ProcessFunction mark_visited = [this](Function* fp) {
|
||||
|
||||
3
3rdparty/spirv-tools/source/opt/function.h
vendored
3
3rdparty/spirv-tools/source/opt/function.h
vendored
@@ -125,6 +125,9 @@ class Function {
|
||||
BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
|
||||
BasicBlock* position);
|
||||
|
||||
BasicBlock* InsertBasicBlockBefore(std::unique_ptr<BasicBlock>&& new_block,
|
||||
BasicBlock* position);
|
||||
|
||||
// Return true if the function calls itself either directly or indirectly.
|
||||
bool IsRecursive() const;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ static const int kSpvAccessChainIndex0IdInIdx = 1;
|
||||
static const int kSpvTypePointerTypeIdInIdx = 1;
|
||||
static const int kSpvTypeArrayLengthIdInIdx = 1;
|
||||
static const int kSpvConstantValueInIdx = 0;
|
||||
static const int kSpvVariableStorageClassInIdx = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -59,36 +60,42 @@ uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id,
|
||||
|
||||
uint32_t InstBindlessCheckPass::CloneOriginalReference(
|
||||
ref_analysis* ref, InstructionBuilder* builder) {
|
||||
// Clone descriptor load
|
||||
Instruction* load_inst = get_def_use_mgr()->GetDef(ref->load_id);
|
||||
Instruction* new_load_inst =
|
||||
builder->AddLoad(load_inst->type_id(),
|
||||
load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx));
|
||||
uid2offset_[new_load_inst->unique_id()] = uid2offset_[load_inst->unique_id()];
|
||||
uint32_t new_load_id = new_load_inst->result_id();
|
||||
get_decoration_mgr()->CloneDecorations(load_inst->result_id(), new_load_id);
|
||||
uint32_t new_image_id = new_load_id;
|
||||
// Clone Image/SampledImage with new load, if needed
|
||||
if (ref->image_id != 0) {
|
||||
Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id);
|
||||
if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
||||
Instruction* new_image_inst = builder->AddBinaryOp(
|
||||
image_inst->type_id(), SpvOpSampledImage, new_load_id,
|
||||
image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx));
|
||||
uid2offset_[new_image_inst->unique_id()] =
|
||||
uid2offset_[image_inst->unique_id()];
|
||||
new_image_id = new_image_inst->result_id();
|
||||
} else {
|
||||
assert(image_inst->opcode() == SpvOp::SpvOpImage && "expecting OpImage");
|
||||
Instruction* new_image_inst =
|
||||
builder->AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id);
|
||||
uid2offset_[new_image_inst->unique_id()] =
|
||||
uid2offset_[image_inst->unique_id()];
|
||||
new_image_id = new_image_inst->result_id();
|
||||
// If original is image based, start by cloning descriptor load
|
||||
uint32_t new_image_id = 0;
|
||||
if (ref->desc_load_id != 0) {
|
||||
Instruction* desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id);
|
||||
Instruction* new_load_inst = builder->AddLoad(
|
||||
desc_load_inst->type_id(),
|
||||
desc_load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx));
|
||||
uid2offset_[new_load_inst->unique_id()] =
|
||||
uid2offset_[desc_load_inst->unique_id()];
|
||||
uint32_t new_load_id = new_load_inst->result_id();
|
||||
get_decoration_mgr()->CloneDecorations(desc_load_inst->result_id(),
|
||||
new_load_id);
|
||||
new_image_id = new_load_id;
|
||||
// Clone Image/SampledImage with new load, if needed
|
||||
if (ref->image_id != 0) {
|
||||
Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id);
|
||||
if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
||||
Instruction* new_image_inst = builder->AddBinaryOp(
|
||||
image_inst->type_id(), SpvOpSampledImage, new_load_id,
|
||||
image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx));
|
||||
uid2offset_[new_image_inst->unique_id()] =
|
||||
uid2offset_[image_inst->unique_id()];
|
||||
new_image_id = new_image_inst->result_id();
|
||||
} else {
|
||||
assert(image_inst->opcode() == SpvOp::SpvOpImage &&
|
||||
"expecting OpImage");
|
||||
Instruction* new_image_inst =
|
||||
builder->AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id);
|
||||
uid2offset_[new_image_inst->unique_id()] =
|
||||
uid2offset_[image_inst->unique_id()];
|
||||
new_image_id = new_image_inst->result_id();
|
||||
}
|
||||
get_decoration_mgr()->CloneDecorations(ref->image_id, new_image_id);
|
||||
}
|
||||
get_decoration_mgr()->CloneDecorations(ref->image_id, new_image_id);
|
||||
}
|
||||
// Clone original reference using new image code
|
||||
// Clone original reference
|
||||
std::unique_ptr<Instruction> new_ref_inst(ref->ref_inst->Clone(context()));
|
||||
uint32_t ref_result_id = ref->ref_inst->result_id();
|
||||
uint32_t new_ref_id = 0;
|
||||
@@ -96,7 +103,9 @@ uint32_t InstBindlessCheckPass::CloneOriginalReference(
|
||||
new_ref_id = TakeNextId();
|
||||
new_ref_inst->SetResultId(new_ref_id);
|
||||
}
|
||||
new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_id});
|
||||
// Update new ref with new image if created
|
||||
if (new_image_id != 0)
|
||||
new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_id});
|
||||
// Register new reference and add to new block
|
||||
Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst));
|
||||
uid2offset_[added_inst->unique_id()] =
|
||||
@@ -106,7 +115,7 @@ uint32_t InstBindlessCheckPass::CloneOriginalReference(
|
||||
return new_ref_id;
|
||||
}
|
||||
|
||||
uint32_t InstBindlessCheckPass::GetDescriptorValueId(Instruction* inst) {
|
||||
uint32_t InstBindlessCheckPass::GetImageId(Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOp::SpvOpImageSampleImplicitLod:
|
||||
case SpvOp::SpvOpImageSampleExplicitLod:
|
||||
@@ -147,27 +156,77 @@ uint32_t InstBindlessCheckPass::GetDescriptorValueId(Instruction* inst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Instruction* InstBindlessCheckPass::GetDescriptorTypeInst(
|
||||
Instruction* var_inst) {
|
||||
uint32_t var_type_id = var_inst->type_id();
|
||||
Instruction* var_type_inst = get_def_use_mgr()->GetDef(var_type_id);
|
||||
uint32_t desc_type_id =
|
||||
var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
|
||||
return get_def_use_mgr()->GetDef(desc_type_id);
|
||||
}
|
||||
|
||||
bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst,
|
||||
ref_analysis* ref) {
|
||||
ref->image_id = GetDescriptorValueId(ref_inst);
|
||||
ref->ref_inst = ref_inst;
|
||||
if (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) {
|
||||
ref->desc_load_id = 0;
|
||||
ref->ptr_id = ref_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
|
||||
Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id);
|
||||
if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return false;
|
||||
ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx);
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id);
|
||||
if (var_inst->opcode() != SpvOp::SpvOpVariable) return false;
|
||||
uint32_t storage_class =
|
||||
var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx);
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassUniform:
|
||||
case SpvStorageClassUniformConstant:
|
||||
case SpvStorageClassStorageBuffer:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
Instruction* desc_type_inst = GetDescriptorTypeInst(var_inst);
|
||||
switch (desc_type_inst->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeRuntimeArray:
|
||||
// A load through a descriptor array will have at least 3 operands. We
|
||||
// do not want to instrument loads of descriptors here which are part of
|
||||
// an image-based reference.
|
||||
if (ptr_inst->NumInOperands() < 3) return false;
|
||||
ref->index_id =
|
||||
ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx);
|
||||
break;
|
||||
default:
|
||||
ref->index_id = 0;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Reference is not load or store. If not an image-based reference, return.
|
||||
ref->image_id = GetImageId(ref_inst);
|
||||
if (ref->image_id == 0) return false;
|
||||
Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id);
|
||||
Instruction* desc_load_inst = nullptr;
|
||||
if (image_inst->opcode() == SpvOp::SpvOpSampledImage) {
|
||||
ref->load_id =
|
||||
ref->desc_load_id =
|
||||
image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx);
|
||||
desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id);
|
||||
} else if (image_inst->opcode() == SpvOp::SpvOpImage) {
|
||||
ref->load_id =
|
||||
ref->desc_load_id =
|
||||
image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx);
|
||||
desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id);
|
||||
} else {
|
||||
ref->load_id = ref->image_id;
|
||||
ref->desc_load_id = ref->image_id;
|
||||
desc_load_inst = image_inst;
|
||||
ref->image_id = 0;
|
||||
}
|
||||
Instruction* load_inst = get_def_use_mgr()->GetDef(ref->load_id);
|
||||
if (load_inst->opcode() != SpvOp::SpvOpLoad) {
|
||||
if (desc_load_inst->opcode() != SpvOp::SpvOpLoad) {
|
||||
// TODO(greg-lunarg): Handle additional possibilities?
|
||||
return false;
|
||||
}
|
||||
ref->ptr_id = load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
|
||||
ref->ptr_id = desc_load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx);
|
||||
Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id);
|
||||
if (ptr_inst->opcode() == SpvOp::SpvOpVariable) {
|
||||
ref->index_id = 0;
|
||||
@@ -189,7 +248,6 @@ bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst,
|
||||
// TODO(greg-lunarg): Handle additional possibilities?
|
||||
return false;
|
||||
}
|
||||
ref->ref_inst = ref_inst;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -260,11 +318,7 @@ void InstBindlessCheckPass::GenBoundsCheckCode(
|
||||
// If index and bound both compile-time constants and index < bound,
|
||||
// return without changing
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id);
|
||||
uint32_t var_type_id = var_inst->type_id();
|
||||
Instruction* var_type_inst = get_def_use_mgr()->GetDef(var_type_id);
|
||||
uint32_t desc_type_id =
|
||||
var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
|
||||
Instruction* desc_type_inst = get_def_use_mgr()->GetDef(desc_type_id);
|
||||
Instruction* desc_type_inst = GetDescriptorTypeInst(var_inst);
|
||||
uint32_t length_id = 0;
|
||||
if (desc_type_inst->opcode() == SpvOpTypeArray) {
|
||||
length_id =
|
||||
|
||||
@@ -118,6 +118,7 @@ class InstBindlessCheckPass : public InstrumentPass {
|
||||
// AnalyzeDescriptorReference. It is necessary and sufficient for further
|
||||
// analysis and regeneration of the reference.
|
||||
typedef struct ref_analysis {
|
||||
uint32_t desc_load_id;
|
||||
uint32_t image_id;
|
||||
uint32_t load_id;
|
||||
uint32_t ptr_id;
|
||||
@@ -131,9 +132,12 @@ class InstBindlessCheckPass : public InstrumentPass {
|
||||
uint32_t CloneOriginalReference(ref_analysis* ref,
|
||||
InstructionBuilder* builder);
|
||||
|
||||
// If |inst| references through a descriptor, (ie references into an image
|
||||
// or buffer), return the id of the value it references. Else return 0.
|
||||
uint32_t GetDescriptorValueId(Instruction* inst);
|
||||
// If |inst| references through an image, return the id of the image it
|
||||
// references through. Else return 0.
|
||||
uint32_t GetImageId(Instruction* inst);
|
||||
|
||||
// Get descriptor type inst of variable |var_inst|.
|
||||
Instruction* GetDescriptorTypeInst(Instruction* var_inst);
|
||||
|
||||
// Analyze descriptor reference |ref_inst| and save components into |ref|.
|
||||
// Return true if |ref_inst| is a descriptor reference, false otherwise.
|
||||
|
||||
12
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
12
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
@@ -483,7 +483,17 @@ bool Instruction::IsFoldableByFoldScalar() const {
|
||||
|
||||
bool Instruction::IsFloatingPointFoldingAllowed() const {
|
||||
// TODO: Add the rules for kernels. For now it will be pessimistic.
|
||||
if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader)) {
|
||||
// For now, do not support capabilities introduced by SPV_KHR_float_controls.
|
||||
if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) ||
|
||||
context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) ||
|
||||
context_->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityDenormFlushToZero) ||
|
||||
context_->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilitySignedZeroInfNanPreserve) ||
|
||||
context_->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityRoundingModeRTZ) ||
|
||||
context_->get_feature_mgr()->HasCapability(
|
||||
SpvCapabilityRoundingModeRTE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "instrument_pass.h"
|
||||
|
||||
#include "source/cfa.h"
|
||||
#include "source/spirv_constant.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -147,10 +148,11 @@ void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id,
|
||||
uint32_t base_offset_id,
|
||||
InstructionBuilder* builder) {
|
||||
// Load and store builtin
|
||||
Instruction* load_inst =
|
||||
builder->AddUnaryOp(GetUintId(), SpvOpLoad, builtin_id);
|
||||
GenDebugOutputFieldCode(base_offset_id, builtin_off, load_inst->result_id(),
|
||||
builder);
|
||||
Instruction* var_inst = get_def_use_mgr()->GetDef(builtin_id);
|
||||
uint32_t type_id = GetPointeeTypeId(var_inst);
|
||||
Instruction* load_inst = builder->AddUnaryOp(type_id, SpvOpLoad, builtin_id);
|
||||
uint32_t val_id = GenUintCastCode(load_inst->result_id(), builder);
|
||||
GenDebugOutputFieldCode(base_offset_id, builtin_off, val_id, builder);
|
||||
}
|
||||
|
||||
void InstrumentPass::GenUintNullOutputCode(uint32_t field_off,
|
||||
@@ -393,6 +395,13 @@ uint32_t InstrumentPass::GetOutputBufferId() {
|
||||
deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding,
|
||||
GetOutputBufferBinding());
|
||||
AddStorageBufferExt();
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output_buffer_id_;
|
||||
}
|
||||
@@ -430,6 +439,13 @@ uint32_t InstrumentPass::GetInputBufferId() {
|
||||
deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding,
|
||||
GetInputBufferBinding());
|
||||
AddStorageBufferExt();
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Add the new buffer to all entry points.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}});
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return input_buffer_id_;
|
||||
}
|
||||
|
||||
11
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
11
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@@ -456,7 +456,16 @@ class IRContext {
|
||||
|
||||
// Return the next available SSA id and increment it. Returns 0 if the
|
||||
// maximum SSA id has been reached.
|
||||
inline uint32_t TakeNextId() { return module()->TakeNextIdBound(); }
|
||||
inline uint32_t TakeNextId() {
|
||||
uint32_t next_id = module()->TakeNextIdBound();
|
||||
if (next_id == 0) {
|
||||
if (consumer()) {
|
||||
std::string message = "ID overflow. Try running compact-ids.";
|
||||
consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
|
||||
}
|
||||
}
|
||||
return next_id;
|
||||
}
|
||||
|
||||
FeatureManager* get_feature_mgr() {
|
||||
if (!feature_mgr_.get()) {
|
||||
|
||||
@@ -225,6 +225,7 @@ Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
|
||||
.RegisterPass(CreateStripAtomicCounterMemoryPass())
|
||||
.RegisterPass(CreateGenerateWebGPUInitializersPass())
|
||||
.RegisterPass(CreateLegalizeVectorShufflePass())
|
||||
.RegisterPass(CreateSplitInvalidUnreachablePass())
|
||||
.RegisterPass(CreateEliminateDeadConstantPass())
|
||||
.RegisterPass(CreateFlattenDecorationPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
@@ -873,4 +874,9 @@ Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
|
||||
MakeUnique<opt::DecomposeInitializedVariablesPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::SplitInvalidUnreachablePass>());
|
||||
}
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
10
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
10
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
@@ -53,14 +53,14 @@ uint32_t Pass::GetPointeeTypeId(const Instruction* ptrInst) const {
|
||||
return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
|
||||
}
|
||||
|
||||
uint32_t Pass::GenerateCopy(Instruction* object_inst, uint32_t new_type_id,
|
||||
uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
||||
Instruction* insertion_position) {
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
|
||||
uint32_t original_type_id = object_inst->type_id();
|
||||
uint32_t original_type_id = object_to_copy->type_id();
|
||||
if (original_type_id == new_type_id) {
|
||||
return object_inst->result_id();
|
||||
return object_to_copy->result_id();
|
||||
}
|
||||
|
||||
InstructionBuilder ir_builder(
|
||||
@@ -86,7 +86,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_inst, uint32_t new_type_id,
|
||||
uint32_t array_length = length_const->AsIntConstant()->GetU32();
|
||||
for (uint32_t i = 0; i < array_length; i++) {
|
||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
original_element_type_id, object_inst->result_id(), {i});
|
||||
original_element_type_id, object_to_copy->result_id(), {i});
|
||||
element_ids.push_back(
|
||||
GenerateCopy(extract, new_element_type_id, insertion_position));
|
||||
}
|
||||
@@ -104,7 +104,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_inst, uint32_t new_type_id,
|
||||
std::vector<uint32_t> element_ids;
|
||||
for (uint32_t i = 0; i < original_types.size(); i++) {
|
||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
|
||||
type_mgr->GetId(original_types[i]), object_to_copy->result_id(), {i});
|
||||
element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
|
||||
insertion_position));
|
||||
}
|
||||
|
||||
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
@@ -65,6 +65,7 @@
|
||||
#include "source/opt/scalar_replacement_pass.h"
|
||||
#include "source/opt/set_spec_constant_default_value_pass.h"
|
||||
#include "source/opt/simplification_pass.h"
|
||||
#include "source/opt/split_invalid_unreachable_pass.h"
|
||||
#include "source/opt/ssa_rewrite_pass.h"
|
||||
#include "source/opt/strength_reduction_pass.h"
|
||||
#include "source/opt/strip_atomic_counter_memory_pass.h"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/spirv_constant.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -38,6 +39,7 @@ Pass::Status PrivateToLocalPass::Process() {
|
||||
return Status::SuccessWithoutChange;
|
||||
|
||||
std::vector<std::pair<Instruction*, Function*>> variables_to_move;
|
||||
std::unordered_set<uint32_t> localized_variables;
|
||||
for (auto& inst : context()->types_values()) {
|
||||
if (inst.opcode() != SpvOpVariable) {
|
||||
continue;
|
||||
@@ -57,6 +59,27 @@ Pass::Status PrivateToLocalPass::Process() {
|
||||
modified = !variables_to_move.empty();
|
||||
for (auto p : variables_to_move) {
|
||||
MoveVariable(p.first, p.second);
|
||||
localized_variables.insert(p.first->result_id());
|
||||
}
|
||||
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// In SPIR-V 1.4 and later entry points must list private storage class
|
||||
// variables that are statically used by the entry point. Go through the
|
||||
// entry points and remove any references to variables that were localized.
|
||||
for (auto& entry : get_module()->entry_points()) {
|
||||
std::vector<Operand> new_operands;
|
||||
for (uint32_t i = 0; i < entry.NumInOperands(); ++i) {
|
||||
// Execution model, function id and name are always kept.
|
||||
if (i < 3 ||
|
||||
!localized_variables.count(entry.GetSingleWordInOperand(i))) {
|
||||
new_operands.push_back(entry.GetInOperand(i));
|
||||
}
|
||||
}
|
||||
if (new_operands.size() != entry.NumInOperands()) {
|
||||
entry.SetInOperands(std::move(new_operands));
|
||||
context()->AnalyzeUses(&entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
|
||||
@@ -165,6 +188,7 @@ void PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
UpdateUses(inst->result_id());
|
||||
break;
|
||||
case SpvOpName:
|
||||
case SpvOpEntryPoint: // entry points will be updated separately.
|
||||
break;
|
||||
default:
|
||||
assert(spvOpcodeIsDecoration(inst->opcode()) &&
|
||||
|
||||
@@ -72,7 +72,9 @@ Pass::Status ScalarReplacementPass::ProcessFunction(Function* function) {
|
||||
bool ScalarReplacementPass::ReplaceVariable(
|
||||
Instruction* inst, std::queue<Instruction*>* worklist) {
|
||||
std::vector<Instruction*> replacements;
|
||||
CreateReplacementVariables(inst, &replacements);
|
||||
if (!CreateReplacementVariables(inst, &replacements)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Instruction*> dead;
|
||||
dead.push_back(inst);
|
||||
@@ -257,7 +259,7 @@ bool ScalarReplacementPass::ReplaceAccessChain(
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScalarReplacementPass::CreateReplacementVariables(
|
||||
bool ScalarReplacementPass::CreateReplacementVariables(
|
||||
Instruction* inst, std::vector<Instruction*>* replacements) {
|
||||
Instruction* type = GetStorageType(inst);
|
||||
|
||||
@@ -302,6 +304,8 @@ void ScalarReplacementPass::CreateReplacementVariables(
|
||||
}
|
||||
|
||||
TransferAnnotations(inst, replacements);
|
||||
return std::find(replacements->begin(), replacements->end(), nullptr) ==
|
||||
replacements->end();
|
||||
}
|
||||
|
||||
void ScalarReplacementPass::TransferAnnotations(
|
||||
@@ -801,7 +805,9 @@ Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
|
||||
const analysis::Constant* null_const = const_mgr->GetConstant(type, {});
|
||||
Instruction* null_inst =
|
||||
const_mgr->GetDefiningInstruction(null_const, type_id);
|
||||
context()->UpdateDefUse(null_inst);
|
||||
if (null_inst != nullptr) {
|
||||
context()->UpdateDefUse(null_inst);
|
||||
}
|
||||
return null_inst;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,8 +117,8 @@ class ScalarReplacementPass : public Pass {
|
||||
// for element of the composite type. Uses of |inst| are updated as
|
||||
// appropriate. If the replacement variables are themselves scalarizable, they
|
||||
// get added to |worklist| for further processing. If any replacement
|
||||
// variable ends up with no uses it is erased. Returns false if any
|
||||
// subsequent access chain is out of bounds.
|
||||
// variable ends up with no uses it is erased. Returns false if the variable
|
||||
// could not be replaced.
|
||||
bool ReplaceVariable(Instruction* inst, std::queue<Instruction*>* worklist);
|
||||
|
||||
// Returns the underlying storage type for |inst|.
|
||||
@@ -145,13 +145,14 @@ class ScalarReplacementPass : public Pass {
|
||||
std::vector<Instruction*>* replacements);
|
||||
|
||||
// Populates |replacements| with a new OpVariable for each element of |inst|.
|
||||
// Returns true if the replacement variables were successfully created.
|
||||
//
|
||||
// |inst| must be an OpVariable of a composite type. New variables are
|
||||
// initialized the same as the corresponding index in |inst|. |replacements|
|
||||
// will contain a variable for each element of the composite with matching
|
||||
// indexes (i.e. the 0'th element of |inst| is the 0'th entry of
|
||||
// |replacements|).
|
||||
void CreateReplacementVariables(Instruction* inst,
|
||||
bool CreateReplacementVariables(Instruction* inst,
|
||||
std::vector<Instruction*>* replacements);
|
||||
|
||||
// Returns the value of an OpConstant of integer type.
|
||||
@@ -217,6 +218,7 @@ class ScalarReplacementPass : public Pass {
|
||||
|
||||
// Returns an instruction defining a null constant with type |type_id|. If
|
||||
// one already exists, it is returned. Otherwise a new one is created.
|
||||
// Returns |nullptr| if the new constant could not be created.
|
||||
Instruction* CreateNullConstant(uint32_t type_id);
|
||||
|
||||
// Maps storage type to a pointer type enclosing that type.
|
||||
|
||||
95
3rdparty/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp
vendored
Normal file
95
3rdparty/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2019 Google LLC.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/split_invalid_unreachable_pass.h"
|
||||
|
||||
#include "source/opt/ir_builder.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status SplitInvalidUnreachablePass::Process() {
|
||||
bool changed = false;
|
||||
std::unordered_set<uint32_t> entry_points;
|
||||
for (auto entry_point : context()->module()->entry_points()) {
|
||||
entry_points.insert(entry_point.GetSingleWordOperand(1));
|
||||
}
|
||||
|
||||
for (auto func = context()->module()->begin();
|
||||
func != context()->module()->end(); ++func) {
|
||||
if (entry_points.find(func->result_id()) == entry_points.end()) continue;
|
||||
std::unordered_set<uint32_t> continue_targets;
|
||||
std::unordered_set<uint32_t> merge_blocks;
|
||||
std::unordered_set<BasicBlock*> unreachable_blocks;
|
||||
for (auto block = func->begin(); block != func->end(); ++block) {
|
||||
unreachable_blocks.insert(&*block);
|
||||
uint32_t continue_target = block->ContinueBlockIdIfAny();
|
||||
if (continue_target != 0) continue_targets.insert(continue_target);
|
||||
uint32_t merge_block = block->MergeBlockIdIfAny();
|
||||
if (merge_block != 0) merge_blocks.insert(merge_block);
|
||||
}
|
||||
|
||||
cfg()->ForEachBlockInPostOrder(
|
||||
func->entry().get(), [&unreachable_blocks](BasicBlock* inner_block) {
|
||||
unreachable_blocks.erase(inner_block);
|
||||
});
|
||||
|
||||
for (auto unreachable : unreachable_blocks) {
|
||||
uint32_t block_id = unreachable->id();
|
||||
if (continue_targets.find(block_id) == continue_targets.end() ||
|
||||
merge_blocks.find(block_id) == merge_blocks.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<Instruction*, uint32_t>> usages;
|
||||
context()->get_def_use_mgr()->ForEachUse(
|
||||
unreachable->GetLabelInst(),
|
||||
[&usages](Instruction* use, uint32_t idx) {
|
||||
if ((use->opcode() == SpvOpLoopMerge && idx == 0) ||
|
||||
use->opcode() == SpvOpSelectionMerge) {
|
||||
usages.push_back(std::make_pair(use, idx));
|
||||
}
|
||||
});
|
||||
|
||||
for (auto usage : usages) {
|
||||
Instruction* use;
|
||||
uint32_t idx;
|
||||
std::tie(use, idx) = usage;
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
std::unique_ptr<Instruction> new_label(
|
||||
new Instruction(context(), SpvOpLabel, 0, new_id, {}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_label.get());
|
||||
std::unique_ptr<BasicBlock> new_block(
|
||||
new BasicBlock(std::move(new_label)));
|
||||
auto* block_ptr = new_block.get();
|
||||
InstructionBuilder builder(context(), new_block.get(),
|
||||
IRContext::kAnalysisDefUse |
|
||||
IRContext::kAnalysisInstrToBlockMapping);
|
||||
builder.AddUnreachable();
|
||||
cfg()->RegisterBlock(block_ptr);
|
||||
(&*func)->InsertBasicBlockBefore(std::move(new_block), unreachable);
|
||||
use->SetInOperand(0, {new_id});
|
||||
get_def_use_mgr()->UpdateDefUse(use);
|
||||
cfg()->AddEdges(block_ptr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
51
3rdparty/spirv-tools/source/opt/split_invalid_unreachable_pass.h
vendored
Normal file
51
3rdparty/spirv-tools/source/opt/split_invalid_unreachable_pass.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
||||
#define SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Attempts to legalize for WebGPU by splitting up invalid unreachable blocks.
|
||||
// Specifically, looking for cases of unreachable merge-blocks and
|
||||
// continue-targets that are used more then once, which is illegal in WebGPU.
|
||||
class SplitInvalidUnreachablePass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "split-invalid-unreachable"; }
|
||||
Status Process() override;
|
||||
|
||||
IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return IRContext::kAnalysisInstrToBlockMapping |
|
||||
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
|
||||
IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
|
||||
IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
|
||||
IRContext::kAnalysisScalarEvolution |
|
||||
IRContext::kAnalysisRegisterPressure |
|
||||
IRContext::kAnalysisValueNumberTable |
|
||||
IRContext::kAnalysisStructuredCFG |
|
||||
IRContext::kAnalysisBuiltinVarId |
|
||||
IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes |
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "source/opt/ir_builder.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -70,6 +71,7 @@ void UpgradeMemoryModel::UpgradeInstructions() {
|
||||
// parameters are implicitly coherent in GLSL450.
|
||||
|
||||
// Upgrade modf and frexp first since they generate new stores.
|
||||
// In SPIR-V 1.4 or later, normalize OpCopyMemory* access operands.
|
||||
for (auto& func : *get_module()) {
|
||||
func.ForEachInst([this](Instruction* inst) {
|
||||
if (inst->opcode() == SpvOpExtInst) {
|
||||
@@ -82,6 +84,29 @@ void UpgradeMemoryModel::UpgradeInstructions() {
|
||||
UpgradeExtInst(inst);
|
||||
}
|
||||
}
|
||||
} else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
if (inst->opcode() == SpvOpCopyMemory ||
|
||||
inst->opcode() == SpvOpCopyMemorySized) {
|
||||
uint32_t start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
|
||||
if (inst->NumInOperands() > start_operand) {
|
||||
auto num_access_words = MemoryAccessNumWords(
|
||||
inst->GetSingleWordInOperand(start_operand));
|
||||
if ((num_access_words + start_operand) == inst->NumInOperands()) {
|
||||
// There is a single memory access operand. Duplicate it to have a
|
||||
// separate operand for both source and target.
|
||||
for (uint32_t i = 0; i < num_access_words; ++i) {
|
||||
auto operand = inst->GetInOperand(start_operand + i);
|
||||
inst->AddOperand(std::move(operand));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add two memory access operands.
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -93,6 +118,7 @@ void UpgradeMemoryModel::UpgradeInstructions() {
|
||||
bool src_volatile = false;
|
||||
bool dst_coherent = false;
|
||||
bool dst_volatile = false;
|
||||
uint32_t start_operand = 0u;
|
||||
SpvScope scope = SpvScopeQueueFamilyKHR;
|
||||
SpvScope src_scope = SpvScopeQueueFamilyKHR;
|
||||
SpvScope dst_scope = SpvScopeQueueFamilyKHR;
|
||||
@@ -129,16 +155,23 @@ void UpgradeMemoryModel::UpgradeInstructions() {
|
||||
kMemory);
|
||||
break;
|
||||
case SpvOpCopyMemory:
|
||||
UpgradeFlags(inst, 2u, dst_coherent, dst_volatile, kAvailability,
|
||||
kMemory);
|
||||
UpgradeFlags(inst, 2u, src_coherent, src_volatile, kVisibility,
|
||||
kMemory);
|
||||
break;
|
||||
case SpvOpCopyMemorySized:
|
||||
UpgradeFlags(inst, 3u, dst_coherent, dst_volatile, kAvailability,
|
||||
kMemory);
|
||||
UpgradeFlags(inst, 3u, src_coherent, src_volatile, kVisibility,
|
||||
kMemory);
|
||||
start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// There are guaranteed to be two memory access operands at this
|
||||
// point so treat source and target separately.
|
||||
uint32_t num_access_words = MemoryAccessNumWords(
|
||||
inst->GetSingleWordInOperand(start_operand));
|
||||
UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile,
|
||||
kAvailability, kMemory);
|
||||
UpgradeFlags(inst, start_operand + num_access_words, src_coherent,
|
||||
src_volatile, kVisibility, kMemory);
|
||||
} else {
|
||||
UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile,
|
||||
kAvailability, kMemory);
|
||||
UpgradeFlags(inst, start_operand, src_coherent, src_volatile,
|
||||
kVisibility, kMemory);
|
||||
}
|
||||
break;
|
||||
case SpvOpImageRead:
|
||||
case SpvOpImageSparseRead:
|
||||
@@ -158,16 +191,49 @@ void UpgradeMemoryModel::UpgradeInstructions() {
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}});
|
||||
}
|
||||
// According to SPV_KHR_vulkan_memory_model, if both available and
|
||||
// visible flags are used the first scope operand is for availability
|
||||
// (writes) and the second is for visibility (reads).
|
||||
if (dst_coherent) {
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
|
||||
}
|
||||
if (src_coherent) {
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
|
||||
if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// There are two memory access operands. The first is for the target and
|
||||
// the second is for the source.
|
||||
if (dst_coherent || src_coherent) {
|
||||
start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u;
|
||||
std::vector<Operand> new_operands;
|
||||
uint32_t num_access_words =
|
||||
MemoryAccessNumWords(inst->GetSingleWordInOperand(start_operand));
|
||||
// The flags were already updated so subtract if we're adding a
|
||||
// scope.
|
||||
if (dst_coherent) --num_access_words;
|
||||
for (uint32_t i = 0; i < start_operand + num_access_words; ++i) {
|
||||
new_operands.push_back(inst->GetInOperand(i));
|
||||
}
|
||||
// Add the target scope if necessary.
|
||||
if (dst_coherent) {
|
||||
new_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
|
||||
}
|
||||
// Copy the remaining current operands.
|
||||
for (uint32_t i = start_operand + num_access_words;
|
||||
i < inst->NumInOperands(); ++i) {
|
||||
new_operands.push_back(inst->GetInOperand(i));
|
||||
}
|
||||
// Add the source scope if necessary.
|
||||
if (src_coherent) {
|
||||
new_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
|
||||
}
|
||||
inst->SetInOperands(std::move(new_operands));
|
||||
}
|
||||
} else {
|
||||
// According to SPV_KHR_vulkan_memory_model, if both available and
|
||||
// visible flags are used the first scope operand is for availability
|
||||
// (writes) and the second is for visibility (reads).
|
||||
if (dst_coherent) {
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
|
||||
}
|
||||
if (src_coherent) {
|
||||
inst->AddOperand(
|
||||
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -636,5 +702,13 @@ void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
|
||||
builder.AddStore(ptr_id, extract_1->result_id());
|
||||
}
|
||||
|
||||
uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) {
|
||||
uint32_t result = 1;
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
#ifndef LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_
|
||||
#define LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_
|
||||
|
||||
#include "pass.h"
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include "pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
@@ -123,6 +123,10 @@ class UpgradeMemoryModel : public Pass {
|
||||
// facilitate adding memory model flags.
|
||||
void UpgradeExtInst(Instruction* modf);
|
||||
|
||||
// Returns the number of words taken up by a memory access argument and its
|
||||
// implied operands.
|
||||
uint32_t MemoryAccessNumWords(uint32_t mask);
|
||||
|
||||
// Caches the result of TraceInstruction. For a given result id and set of
|
||||
// indices, stores whether that combination is coherent and/or volatile.
|
||||
std::unordered_map<std::pair<uint32_t, std::vector<uint32_t>>,
|
||||
|
||||
28
3rdparty/spirv-tools/source/spirv_target_env.cpp
vendored
28
3rdparty/spirv-tools/source/spirv_target_env.cpp
vendored
@@ -15,6 +15,7 @@
|
||||
#include "source/spirv_target_env.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "source/spirv_constant.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
@@ -61,6 +62,10 @@ const char* spvTargetEnvDescription(spv_target_env env) {
|
||||
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return "SPIR-V 1.3 (under WIP WebGPU semantics)";
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return "SPIR-V 1.4";
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return "SPIR-V 1.4 (under Vulkan 1.1 semantics)";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -91,6 +96,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 3);
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 4);
|
||||
}
|
||||
return SPV_SPIRV_VERSION_WORD(0, 0);
|
||||
}
|
||||
@@ -99,7 +107,10 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
||||
auto match = [s](const char* b) {
|
||||
return s && (0 == strncmp(s, b, strlen(b)));
|
||||
};
|
||||
if (match("vulkan1.0")) {
|
||||
if (match("vulkan1.1spv1.4")) {
|
||||
if (env) *env = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
|
||||
return true;
|
||||
} else if (match("vulkan1.0")) {
|
||||
if (env) *env = SPV_ENV_VULKAN_1_0;
|
||||
return true;
|
||||
} else if (match("vulkan1.1")) {
|
||||
@@ -117,6 +128,9 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
||||
} else if (match("spv1.3")) {
|
||||
if (env) *env = SPV_ENV_UNIVERSAL_1_3;
|
||||
return true;
|
||||
} else if (match("spv1.4")) {
|
||||
if (env) *env = SPV_ENV_UNIVERSAL_1_4;
|
||||
return true;
|
||||
} else if (match("opencl1.2embedded")) {
|
||||
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2;
|
||||
return true;
|
||||
@@ -185,9 +199,11 @@ bool spvIsVulkanEnv(spv_target_env env) {
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return false;
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -207,6 +223,8 @@ bool spvIsOpenCLEnv(spv_target_env env) {
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return false;
|
||||
case SPV_ENV_OPENCL_1_2:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_1_2:
|
||||
@@ -242,6 +260,8 @@ bool spvIsWebGPUEnv(spv_target_env env) {
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return false;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return true;
|
||||
@@ -273,7 +293,8 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
||||
return "OpenGL";
|
||||
}
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_VULKAN_1_1: {
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4: {
|
||||
return "Vulkan";
|
||||
}
|
||||
case SPV_ENV_WEBGPU_0: {
|
||||
@@ -282,7 +303,8 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
||||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3: {
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_UNIVERSAL_1_4: {
|
||||
return "Universal";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/spirv_validator_options.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "source/spirv_validator_options.h"
|
||||
|
||||
bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* type) {
|
||||
auto match = [s](const char* b) {
|
||||
return s && (0 == strncmp(s, b, strlen(b)));
|
||||
@@ -90,11 +90,22 @@ void spvValidatorOptionsSetRelaxLogicalPointer(spv_validator_options options,
|
||||
options->relax_logical_pointer = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetBeforeHlslLegalization(spv_validator_options options,
|
||||
bool val) {
|
||||
options->before_hlsl_legalization = val;
|
||||
options->relax_logical_pointer = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetRelaxBlockLayout(spv_validator_options options,
|
||||
bool val) {
|
||||
options->relax_block_layout = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetUniformBufferStandardLayout(
|
||||
spv_validator_options options, bool val) {
|
||||
options->uniform_buffer_standard_layout = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options,
|
||||
bool val) {
|
||||
options->scalar_block_layout = val;
|
||||
|
||||
@@ -43,15 +43,19 @@ struct spv_validator_options_t {
|
||||
relax_struct_store(false),
|
||||
relax_logical_pointer(false),
|
||||
relax_block_layout(false),
|
||||
uniform_buffer_standard_layout(false),
|
||||
scalar_block_layout(false),
|
||||
skip_block_layout(false) {}
|
||||
skip_block_layout(false),
|
||||
before_hlsl_legalization(false) {}
|
||||
|
||||
validator_universal_limits_t universal_limits_;
|
||||
bool relax_struct_store;
|
||||
bool relax_logical_pointer;
|
||||
bool relax_block_layout;
|
||||
bool uniform_buffer_standard_layout;
|
||||
bool scalar_block_layout;
|
||||
bool skip_block_layout;
|
||||
bool before_hlsl_legalization;
|
||||
};
|
||||
|
||||
#endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_
|
||||
|
||||
2
3rdparty/spirv-tools/source/table.cpp
vendored
2
3rdparty/spirv-tools/source/table.cpp
vendored
@@ -37,7 +37,9 @@ spv_context spvContextCreate(spv_target_env env) {
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
|
||||
5
3rdparty/spirv-tools/source/table.h
vendored
5
3rdparty/spirv-tools/source/table.h
vendored
@@ -15,9 +15,8 @@
|
||||
#ifndef SOURCE_TABLE_H_
|
||||
#define SOURCE_TABLE_H_
|
||||
|
||||
#include "source/latest_version_spirv_header.h"
|
||||
|
||||
#include "source/extensions.h"
|
||||
#include "source/latest_version_spirv_header.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
typedef struct spv_opcode_desc_t {
|
||||
@@ -42,6 +41,7 @@ typedef struct spv_opcode_desc_t {
|
||||
// extensions. ~0u means reserved for future use. ~0u and non-empty extension
|
||||
// lists means only available in extensions.
|
||||
const uint32_t minVersion;
|
||||
const uint32_t lastVersion;
|
||||
} spv_opcode_desc_t;
|
||||
|
||||
typedef struct spv_operand_desc_t {
|
||||
@@ -60,6 +60,7 @@ typedef struct spv_operand_desc_t {
|
||||
// extensions. ~0u means reserved for future use. ~0u and non-empty extension
|
||||
// lists means only available in extensions.
|
||||
const uint32_t minVersion;
|
||||
const uint32_t lastVersion;
|
||||
} spv_operand_desc_t;
|
||||
|
||||
typedef struct spv_operand_desc_group_t {
|
||||
|
||||
71
3rdparty/spirv-tools/source/val/construct.cpp
vendored
71
3rdparty/spirv-tools/source/val/construct.cpp
vendored
@@ -19,6 +19,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
@@ -105,7 +106,8 @@ Construct::ConstructBlockSet Construct::blocks(Function* function) const {
|
||||
// A selection construct nested directly within the loop construct is also
|
||||
// at the same depth. It is valid, however, to branch directly to the
|
||||
// continue target from within the selection construct.
|
||||
if (block_depth == header_depth && type() == ConstructType::kSelection &&
|
||||
if (block != header && block_depth == header_depth &&
|
||||
type() == ConstructType::kSelection &&
|
||||
block->is_type(kBlockTypeContinue)) {
|
||||
// Continued to outer construct.
|
||||
continue;
|
||||
@@ -126,5 +128,72 @@ Construct::ConstructBlockSet Construct::blocks(Function* function) const {
|
||||
return construct_blocks;
|
||||
}
|
||||
|
||||
bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
|
||||
// Structured Exits:
|
||||
// - Selection:
|
||||
// - branch to its merge
|
||||
// - branch to nearest enclosing loop merge or continue
|
||||
// - Loop:
|
||||
// - branch to its merge
|
||||
// - branch to its continue
|
||||
// - Continue:
|
||||
// - branch to loop header
|
||||
// - branch to loop merge
|
||||
//
|
||||
// Note: we will never see a case construct here.
|
||||
assert(type() != ConstructType::kCase);
|
||||
if (type() == ConstructType::kLoop) {
|
||||
auto header = entry_block();
|
||||
auto terminator = header->terminator();
|
||||
auto index = terminator - &_.ordered_instructions()[0];
|
||||
auto merge_inst = &_.ordered_instructions()[index - 1];
|
||||
auto merge_block_id = merge_inst->GetOperandAs<uint32_t>(0u);
|
||||
auto continue_block_id = merge_inst->GetOperandAs<uint32_t>(1u);
|
||||
if (dest->id() == merge_block_id || dest->id() == continue_block_id) {
|
||||
return true;
|
||||
}
|
||||
} else if (type() == ConstructType::kContinue) {
|
||||
auto loop_construct = corresponding_constructs()[0];
|
||||
auto header = loop_construct->entry_block();
|
||||
auto terminator = header->terminator();
|
||||
auto index = terminator - &_.ordered_instructions()[0];
|
||||
auto merge_inst = &_.ordered_instructions()[index - 1];
|
||||
auto merge_block_id = merge_inst->GetOperandAs<uint32_t>(0u);
|
||||
if (dest == header || dest->id() == merge_block_id) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
assert(type() == ConstructType::kSelection);
|
||||
if (dest == exit_block()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto header = entry_block();
|
||||
auto block = header;
|
||||
while (block) {
|
||||
auto terminator = block->terminator();
|
||||
auto index = terminator - &_.ordered_instructions()[0];
|
||||
auto merge_inst = &_.ordered_instructions()[index - 1];
|
||||
if (merge_inst->opcode() == SpvOpLoopMerge) {
|
||||
auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
|
||||
auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
|
||||
if (merge_block->dominates(*header)) {
|
||||
block = block->immediate_dominator();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto continue_target = merge_inst->GetOperandAs<uint32_t>(1u);
|
||||
if (dest->id() == merge_target || dest->id() == continue_target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
block = block->immediate_dominator();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
17
3rdparty/spirv-tools/source/val/construct.h
vendored
17
3rdparty/spirv-tools/source/val/construct.h
vendored
@@ -23,6 +23,7 @@
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
class ValidationState_t;
|
||||
|
||||
/// Functor for ordering BasicBlocks. BasicBlock pointers must not be null.
|
||||
struct less_than_id {
|
||||
@@ -109,6 +110,22 @@ class Construct {
|
||||
// calculated.
|
||||
ConstructBlockSet blocks(Function* function) const;
|
||||
|
||||
// Returns true if |dest| is structured exit from the construct. Structured
|
||||
// exits depend on the construct type.
|
||||
// Selection:
|
||||
// * branch to the associated merge
|
||||
// * branch to the merge or continue of the innermost loop containing the
|
||||
// selection
|
||||
// Loop:
|
||||
// * branch to the associated merge or continue
|
||||
// Continue:
|
||||
// * back-edge to the associated loop header
|
||||
// * branch to the associated loop merge
|
||||
//
|
||||
// Note: the validator does not generate case constructs. Switches are
|
||||
// checked separately from other constructs.
|
||||
bool IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const;
|
||||
|
||||
private:
|
||||
/// The type of the construct
|
||||
ConstructType type_;
|
||||
|
||||
21
3rdparty/spirv-tools/source/val/validate.cpp
vendored
21
3rdparty/spirv-tools/source/val/validate.cpp
vendored
@@ -14,10 +14,9 @@
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -52,21 +51,6 @@ namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
// TODO(umar): Validate header
|
||||
// TODO(umar): The binary parser validates the magic word, and the length of the
|
||||
// header, but nothing else.
|
||||
spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t) {
|
||||
// Record the ID bound so that the validator can ensure no ID is out of bound.
|
||||
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
|
||||
_.setIdBound(id_bound);
|
||||
_.setGenerator(generator);
|
||||
_.setVersion(version);
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Parses OpExtension instruction and registers extension.
|
||||
void RegisterExtension(ValidationState_t& _,
|
||||
const spv_parsed_instruction_t* inst) {
|
||||
@@ -282,7 +266,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
|
||||
// Parse the module and perform inline validation checks. These checks do
|
||||
// not require the the knowledge of the whole module.
|
||||
if (auto error = spvBinaryParse(&context, vstate, words, num_words, setHeader,
|
||||
if (auto error = spvBinaryParse(&context, vstate, words, num_words,
|
||||
/*parsed_header =*/nullptr,
|
||||
ProcessInstruction, pDiagnostic)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -185,6 +185,21 @@ std::string LogStringForDecoration(uint32_t decoration) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
// Returns true if the decoration takes ID parameters.
|
||||
// TODO(dneto): This can be generated from the grammar.
|
||||
bool DecorationTakesIdParameters(uint32_t type) {
|
||||
switch (static_cast<SpvDecoration>(type)) {
|
||||
case SpvDecorationUniformId:
|
||||
case SpvDecorationAlignmentId:
|
||||
case SpvDecorationMaxByteOffsetId:
|
||||
case SpvDecorationHlslCounterBufferGOOGLE:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
||||
if (decoration == SpvDecorationSpecId) {
|
||||
@@ -205,10 +220,27 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "' is not valid for the WebGPU execution environment.";
|
||||
}
|
||||
|
||||
if (DecorationTakesIdParameters(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Decorations taking ID parameters may not be used with "
|
||||
"OpDecorateId";
|
||||
}
|
||||
// TODO: Add validations for all decorations.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
||||
if (!DecorationTakesIdParameters(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Decorations that don't take ID parameters may not be used with "
|
||||
"OpDecorateId";
|
||||
}
|
||||
// TODO: Add validations for these decorations.
|
||||
// UniformId is covered elsewhere.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateMemberDecorate(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
|
||||
@@ -340,7 +372,8 @@ spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
|
||||
spv_result_t RegisterDecorations(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpDecorate: {
|
||||
case SpvOpDecorate:
|
||||
case SpvOpDecorateId: {
|
||||
const uint32_t target_id = inst->word(1);
|
||||
const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(2));
|
||||
std::vector<uint32_t> dec_params;
|
||||
@@ -415,6 +448,11 @@ spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
|
||||
case SpvOpDecorate:
|
||||
if (auto error = ValidateDecorate(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpDecorateId:
|
||||
if (auto error = ValidateDecorateId(_, inst)) return error;
|
||||
break;
|
||||
// TODO(dneto): SpvOpDecorateStringGOOGLE
|
||||
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
|
||||
case SpvOpMemberDecorate:
|
||||
if (auto error = ValidateMemberDecorate(_, inst)) return error;
|
||||
break;
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
// Validates correctness of barrier SPIR-V instructions.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
@@ -24,6 +22,7 @@
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/util/bitutils.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validate_memory_semantics.h"
|
||||
#include "source/val/validate_scopes.h"
|
||||
#include "source/val/validation_state.h"
|
||||
@@ -38,8 +37,7 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
switch (opcode) {
|
||||
case SpvOpControlBarrier: {
|
||||
if (spvVersionForTargetEnv(_.context()->target_env) <
|
||||
SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation(
|
||||
[](SpvExecutionModel model, std::string* message) {
|
||||
|
||||
100
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
100
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@@ -230,6 +230,78 @@ spv_result_t ValidateReturnValue(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateLoopMerge(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto merge_id = inst->GetOperandAs<uint32_t>(0);
|
||||
const auto merge = _.FindDef(merge_id);
|
||||
if (!merge || merge->opcode() != SpvOpLabel) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Merge Block " << _.getIdName(merge_id) << " must be an OpLabel";
|
||||
}
|
||||
|
||||
const auto continue_id = inst->GetOperandAs<uint32_t>(1);
|
||||
const auto continue_target = _.FindDef(continue_id);
|
||||
if (!continue_target || continue_target->opcode() != SpvOpLabel) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Continue Target " << _.getIdName(continue_id)
|
||||
<< " must be an OpLabel";
|
||||
}
|
||||
|
||||
if (merge_id == continue_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Merge Block and Continue Target must be different ids";
|
||||
}
|
||||
|
||||
const auto loop_control = inst->GetOperandAs<uint32_t>(2);
|
||||
if ((loop_control >> SpvLoopControlUnrollShift) & 0x1 &&
|
||||
(loop_control >> SpvLoopControlDontUnrollShift) & 0x1) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Unroll and DontUnroll loop controls must not both be specified";
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 &&
|
||||
(loop_control >> SpvLoopControlPeelCountShift) & 0x1) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PeelCount and DontUnroll "
|
||||
"loop controls must not "
|
||||
"both be specified";
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 &&
|
||||
(loop_control >> SpvLoopControlPartialCountShift) & 0x1) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PartialCount and "
|
||||
"DontUnroll loop controls "
|
||||
"must not both be specified";
|
||||
}
|
||||
|
||||
uint32_t operand = 3;
|
||||
if ((loop_control >> SpvLoopControlDependencyLengthShift) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlMinIterationsShift) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlMaxIterationsShift) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlIterationMultipleShift) & 0x1) {
|
||||
if (inst->operands().size() < operand ||
|
||||
inst->GetOperandAs<uint32_t>(operand) == 0) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst) << "IterationMultiple loop "
|
||||
"control operand must be "
|
||||
"greater than zero";
|
||||
}
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlPeelCountShift) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
if ((loop_control >> SpvLoopControlPartialCountShift) & 0x1) {
|
||||
++operand;
|
||||
}
|
||||
|
||||
// That the right number of operands is present is checked by the parser. The
|
||||
// above code tracks operands for expanded validation checking in the future.
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void printDominatorList(const BasicBlock& b) {
|
||||
@@ -580,16 +652,27 @@ spv_result_t StructuredControlFlowChecks(
|
||||
}
|
||||
}
|
||||
|
||||
// Check that for all non-header blocks, all predecessors are within this
|
||||
// construct.
|
||||
Construct::ConstructBlockSet construct_blocks = construct.blocks(function);
|
||||
for (auto block : construct_blocks) {
|
||||
std::string construct_name, header_name, exit_name;
|
||||
std::tie(construct_name, header_name, exit_name) =
|
||||
ConstructNames(construct.type());
|
||||
// Check that all exits from the construct are via structured exits.
|
||||
for (auto succ : *block->successors()) {
|
||||
if (block->reachable() && !construct_blocks.count(succ) &&
|
||||
!construct.IsStructuredExit(_, succ)) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "block <ID> " << _.getIdName(block->id()) << " exits the "
|
||||
<< construct_name << " headed by <ID> "
|
||||
<< _.getIdName(header->id())
|
||||
<< ", but not via a structured exit";
|
||||
}
|
||||
}
|
||||
if (block == header) continue;
|
||||
// Check that for all non-header blocks, all predecessors are within this
|
||||
// construct.
|
||||
for (auto pred : *block->predecessors()) {
|
||||
if (pred->reachable() && !construct_blocks.count(pred)) {
|
||||
std::string construct_name, header_name, exit_name;
|
||||
std::tie(construct_name, header_name, exit_name) =
|
||||
ConstructNames(construct.type());
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id()))
|
||||
<< "block <ID> " << pred->id() << " branches to the "
|
||||
<< construct_name << " construct, but not to the "
|
||||
@@ -608,6 +691,7 @@ spv_result_t StructuredControlFlowChecks(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -764,7 +848,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
||||
auto edges = CFA<BasicBlock>::CalculateDominators(
|
||||
postorder, function.AugmentedCFGPredecessorsFunction());
|
||||
for (auto edge : edges) {
|
||||
edge.first->SetImmediateDominator(edge.second);
|
||||
if (edge.first != edge.second)
|
||||
edge.first->SetImmediateDominator(edge.second);
|
||||
}
|
||||
|
||||
/// calculate post dominators
|
||||
@@ -931,6 +1016,9 @@ spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
|
||||
case SpvOpSwitch:
|
||||
if (auto error = ValidateSwitch(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpLoopMerge:
|
||||
if (auto error = ValidateLoopMerge(_, inst)) return error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -513,6 +513,24 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyLogical(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto result_type = _.FindDef(inst->type_id());
|
||||
const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
|
||||
const auto source_type = _.FindDef(source->type_id());
|
||||
if (!source_type || !result_type || source_type == result_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must not equal the Operand type";
|
||||
}
|
||||
|
||||
if (!_.LogicallyMatch(source_type, result_type, false)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type does not logically match the Operand type";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Validates correctness of composite instructions.
|
||||
@@ -534,6 +552,8 @@ spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return ValidateCopyObject(_, inst);
|
||||
case SpvOpTranspose:
|
||||
return ValidateTranspose(_, inst);
|
||||
case SpvOpCopyLogical:
|
||||
return ValidateCopyLogical(_, inst);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -364,7 +364,8 @@ spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
|
||||
if (!_.features().uconvert_spec_constant_op &&
|
||||
!_.HasCapability(SpvCapabilityKernel)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "UConvert requires Kernel capability or extension "
|
||||
<< "Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability or extension "
|
||||
"SPV_AMD_gpu_shader_int16";
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
@@ -25,8 +23,10 @@
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/spirv_validator_options.h"
|
||||
#include "source/val/validate_scopes.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -384,6 +384,10 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
ValidationState_t& vstate) {
|
||||
if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
|
||||
|
||||
// blockRules are the same as bufferBlock rules if the uniform buffer
|
||||
// standard layout extension is being used.
|
||||
if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
|
||||
|
||||
// Relaxed layout and scalar layout can both be in effect at the same time.
|
||||
// For example, relaxed layout is implied by Vulkan 1.1. But scalar layout
|
||||
// is more permissive than relaxed layout.
|
||||
@@ -684,6 +688,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
int num_builtin_inputs = 0;
|
||||
int num_builtin_outputs = 0;
|
||||
for (const auto& desc : descs) {
|
||||
std::unordered_set<Instruction*> seen_vars;
|
||||
for (auto interface : desc.interfaces) {
|
||||
Instruction* var_instr = vstate.FindDef(interface);
|
||||
if (!var_instr || SpvOpVariable != var_instr->opcode()) {
|
||||
@@ -694,14 +699,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
}
|
||||
const SpvStorageClass storage_class =
|
||||
var_instr->GetOperandAs<SpvStorageClass>(2);
|
||||
if (storage_class != SpvStorageClassInput &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class "
|
||||
<< storage_class << " for Entry Point id " << entry_point
|
||||
<< ".";
|
||||
if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Starting in 1.4, OpEntryPoint must list all global variables
|
||||
// it statically uses and those interfaces must be unique.
|
||||
if (storage_class == SpvStorageClassFunction) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces should only list global "
|
||||
"variables";
|
||||
}
|
||||
|
||||
if (!seen_vars.insert(var_instr).second) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "Non-unique OpEntryPoint interface "
|
||||
<< vstate.getIdName(interface) << " is disallowed";
|
||||
}
|
||||
} else {
|
||||
if (storage_class != SpvStorageClassInput &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class "
|
||||
<< storage_class << " for Entry Point id " << entry_point
|
||||
<< ".";
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t ptr_id = var_instr->word(1);
|
||||
@@ -964,10 +985,12 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
vstate.RegisterPointerToUniformBlock(ptrInst->id());
|
||||
vstate.RegisterStructForUniformBlock(id);
|
||||
}
|
||||
if ((uniform && bufferDeco) || (storage_buffer && blockDeco)) {
|
||||
if ((uniform && bufferDeco) ||
|
||||
((storage_buffer || phys_storage_buffer) && blockDeco)) {
|
||||
vstate.RegisterPointerToStorageBuffer(ptrInst->id());
|
||||
vstate.RegisterStructForStorageBuffer(id);
|
||||
}
|
||||
|
||||
if (blockRules || bufferRules) {
|
||||
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
|
||||
spv_result_t recursive_status = SPV_SUCCESS;
|
||||
@@ -1257,34 +1280,51 @@ spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
|
||||
if (decoration.struct_member_index() == Decoration::kInvalidMember) {
|
||||
// The target must be a memory object declaration.
|
||||
// First, it must be a variable or function parameter.
|
||||
if (inst.opcode() != SpvOpVariable &&
|
||||
inst.opcode() != SpvOpFunctionParameter) {
|
||||
const auto opcode = inst.opcode();
|
||||
const auto type_id = inst.type_id();
|
||||
if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Target of NonWritable decoration must be a memory object "
|
||||
"declaration (a variable or a function parameter)";
|
||||
}
|
||||
// Second, it must point to a UBO, SSBO, or storage image.
|
||||
const auto type_id = inst.type_id();
|
||||
if (!vstate.IsPointerToUniformBlock(type_id) &&
|
||||
!vstate.IsPointerToStorageBuffer(type_id) &&
|
||||
!vstate.IsPointerToStorageImage(type_id)) {
|
||||
const auto var_storage_class = opcode == SpvOpVariable
|
||||
? inst.GetOperandAs<SpvStorageClass>(2)
|
||||
: SpvStorageClassMax;
|
||||
if ((var_storage_class == SpvStorageClassFunction ||
|
||||
var_storage_class == SpvStorageClassPrivate) &&
|
||||
vstate.features().nonwritable_var_in_function_or_private) {
|
||||
// New permitted feature in SPIR-V 1.4.
|
||||
} else if (
|
||||
// It may point to a UBO, SSBO, or storage image.
|
||||
vstate.IsPointerToUniformBlock(type_id) ||
|
||||
vstate.IsPointerToStorageBuffer(type_id) ||
|
||||
vstate.IsPointerToStorageImage(type_id)) {
|
||||
} else {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Target of NonWritable decoration is invalid: must point to a "
|
||||
"storage image, uniform block, or storage buffer";
|
||||
"storage image, uniform block, "
|
||||
<< (vstate.features().nonwritable_var_in_function_or_private
|
||||
? "storage buffer, or variable in Private or Function "
|
||||
"storage class"
|
||||
: "or storage buffer");
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if validation rules are satisfied for Uniform
|
||||
// decorations. Otherwise emits a diagnostic and returns something other than
|
||||
// SPV_SUCCESS. Assumes each decoration on a group has been propagated down to
|
||||
// the group members.
|
||||
// Returns SPV_SUCCESS if validation rules are satisfied for Uniform or
|
||||
// UniformId decorations. Otherwise emits a diagnostic and returns something
|
||||
// other than SPV_SUCCESS. Assumes each decoration on a group has been
|
||||
// propagated down to the group members. The |inst| parameter is the object
|
||||
// being decorated.
|
||||
spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
|
||||
const Instruction& inst,
|
||||
const Decoration&) {
|
||||
// Uniform must decorate an "object"
|
||||
const Decoration& decoration) {
|
||||
const char* const dec_name =
|
||||
decoration.dec_type() == SpvDecorationUniform ? "Uniform" : "UniformId";
|
||||
|
||||
// Uniform or UniformId must decorate an "object"
|
||||
// - has a result ID
|
||||
// - is an instantiation of a non-void type. So it has a type ID, and that
|
||||
// type is not void.
|
||||
@@ -1293,19 +1333,33 @@ spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
|
||||
|
||||
if (inst.type_id() == 0) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to a non-object";
|
||||
<< dec_name << " decoration applied to a non-object";
|
||||
}
|
||||
if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
|
||||
if (type_inst->opcode() == SpvOpTypeVoid) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to a value with void type";
|
||||
<< dec_name << " decoration applied to a value with void type";
|
||||
}
|
||||
} else {
|
||||
// We might never get here because this would have been rejected earlier in
|
||||
// the flow.
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Uniform decoration applied to an object with invalid type";
|
||||
<< dec_name << " decoration applied to an object with invalid type";
|
||||
}
|
||||
|
||||
// Use of Uniform with OpDecorate is checked elsewhere.
|
||||
// Use of UniformId with OpDecorateId is checked elsewhere.
|
||||
|
||||
if (decoration.dec_type() == SpvDecorationUniformId) {
|
||||
assert(decoration.params().size() == 1 &&
|
||||
"Grammar ensures UniformId has one parameter");
|
||||
|
||||
// The scope id is an execution scope.
|
||||
if (auto error =
|
||||
ValidateExecutionScope(vstate, &inst, decoration.params()[0]))
|
||||
return error;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1365,7 +1419,6 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
|
||||
// been propagated down to the group members.
|
||||
if (inst->opcode() == SpvOpDecorationGroup) continue;
|
||||
|
||||
// Validates FPRoundingMode decoration
|
||||
for (const auto& decoration : decorations) {
|
||||
switch (decoration.dec_type()) {
|
||||
case SpvDecorationFPRoundingMode:
|
||||
@@ -1376,6 +1429,7 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
|
||||
PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
case SpvDecorationUniform:
|
||||
case SpvDecorationUniformId:
|
||||
PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
case SpvDecorationNoSignedWrap:
|
||||
|
||||
@@ -12,27 +12,45 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
// Returns true if |a| and |b| are instruction defining pointers that point to
|
||||
// the same type.
|
||||
bool ArePointersToSameType(val::Instruction* a, val::Instruction* b) {
|
||||
// Returns true if |a| and |b| are instructions defining pointers that point to
|
||||
// types logically match and the decorations that apply to |b| are a subset
|
||||
// of the decorations that apply to |a|.
|
||||
bool DoPointeesLogicallyMatch(val::Instruction* a, val::Instruction* b,
|
||||
ValidationState_t& _) {
|
||||
if (a->opcode() != SpvOpTypePointer || b->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& dec_a = _.id_decorations(a->id());
|
||||
const auto& dec_b = _.id_decorations(b->id());
|
||||
for (const auto& dec : dec_b) {
|
||||
if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t a_type = a->GetOperandAs<uint32_t>(2);
|
||||
return a_type && (a_type == b->GetOperandAs<uint32_t>(2));
|
||||
uint32_t b_type = b->GetOperandAs<uint32_t>(2);
|
||||
|
||||
if (a_type == b_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Instruction* a_type_inst = _.FindDef(a_type);
|
||||
Instruction* b_type_inst = _.FindDef(b_type);
|
||||
|
||||
return _.LogicallyMatch(a_type_inst, b_type_inst, true);
|
||||
}
|
||||
|
||||
spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) {
|
||||
@@ -256,14 +274,14 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
|
||||
const auto parameter_type_id =
|
||||
function_type->GetOperandAs<uint32_t>(param_index);
|
||||
const auto parameter_type = _.FindDef(parameter_type_id);
|
||||
if (!parameter_type ||
|
||||
(argument_type->id() != parameter_type->id() &&
|
||||
!(_.options()->relax_logical_pointer &&
|
||||
ArePointersToSameType(argument_type, parameter_type)))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpFunctionCall Argument <id> '" << _.getIdName(argument_id)
|
||||
<< "'s type does not match Function <id> '"
|
||||
<< _.getIdName(parameter_type_id) << "'s parameter type.";
|
||||
if (!parameter_type || argument_type->id() != parameter_type->id()) {
|
||||
if (!_.options()->before_hlsl_legalization ||
|
||||
!DoPointeesLogicallyMatch(argument_type, parameter_type, _)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpFunctionCall Argument <id> '" << _.getIdName(argument_id)
|
||||
<< "'s type does not match Function <id> '"
|
||||
<< _.getIdName(parameter_type_id) << "'s parameter type.";
|
||||
}
|
||||
}
|
||||
|
||||
if (_.addressing_model() == SpvAddressingModelLogical) {
|
||||
|
||||
@@ -63,6 +63,8 @@ bool CheckAllImageOperandsHandled() {
|
||||
case SpvImageOperandsMakeTexelVisibleKHRMask:
|
||||
case SpvImageOperandsNonPrivateTexelKHRMask:
|
||||
case SpvImageOperandsVolatileTexelKHRMask:
|
||||
case SpvImageOperandsSignExtendMask:
|
||||
case SpvImageOperandsZeroExtendMask:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -218,10 +220,12 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
const SpvOp opcode = inst->opcode();
|
||||
const size_t num_words = inst->words().size();
|
||||
|
||||
// NonPrivate and Volatile take no operand words.
|
||||
// NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words.
|
||||
const uint32_t mask_bits_having_operands =
|
||||
mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
|
||||
SpvImageOperandsVolatileTexelKHRMask);
|
||||
SpvImageOperandsVolatileTexelKHRMask |
|
||||
SpvImageOperandsSignExtendMask |
|
||||
SpvImageOperandsZeroExtendMask);
|
||||
size_t expected_num_image_operand_words =
|
||||
spvtools::utils::CountSetBits(mask_bits_having_operands);
|
||||
if (mask & SpvImageOperandsGradMask) {
|
||||
@@ -541,6 +545,32 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
|
||||
if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
|
||||
}
|
||||
|
||||
if (mask & SpvImageOperandsSignExtendMask) {
|
||||
// Checked elsewhere: SPIR-V 1.4 version or later.
|
||||
|
||||
// "The texel value is converted to the target value via sign extension.
|
||||
// Only valid when the texel type is a scalar or vector of integer type."
|
||||
//
|
||||
// We don't have enough information to know what the texel type is.
|
||||
// In OpenCL, knowledge is deferred until runtime: the image SampledType is
|
||||
// void, and the Format is Unknown.
|
||||
// In Vulkan, the texel type is only known in all cases by the pipeline
|
||||
// setup.
|
||||
}
|
||||
|
||||
if (mask & SpvImageOperandsZeroExtendMask) {
|
||||
// Checked elsewhere: SPIR-V 1.4 version or later.
|
||||
|
||||
// "The texel value is converted to the target value via zero extension.
|
||||
// Only valid when the texel type is a scalar or vector of integer type."
|
||||
//
|
||||
// We don't have enough information to know what the texel type is.
|
||||
// In OpenCL, knowledge is deferred until runtime: the image SampledType is
|
||||
// void, and the Format is Unknown.
|
||||
// In Vulkan, the texel type is only known in all cases by the pipeline
|
||||
// setup.
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
|
||||
// Performs validation on instructions that appear inside of a SPIR-V block.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -35,6 +34,7 @@
|
||||
#include "source/spirv_validator_options.h"
|
||||
#include "source/util/string_utils.h"
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -98,21 +98,72 @@ CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state,
|
||||
return CapabilitySet();
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if, for the given operand, the target environment
|
||||
// satsifies minimum version requirements, or if the module declares an
|
||||
// enabling extension for the operand. Otherwise emit a diagnostic and
|
||||
// return an error code.
|
||||
spv_result_t OperandVersionExtensionCheck(
|
||||
ValidationState_t& _, const Instruction* inst, size_t which_operand,
|
||||
const spv_operand_desc_t& operand_desc, uint32_t word) {
|
||||
const uint32_t module_version = _.version();
|
||||
const uint32_t operand_min_version = operand_desc.minVersion;
|
||||
const uint32_t operand_last_version = operand_desc.lastVersion;
|
||||
const bool reserved = operand_min_version == 0xffffffffu;
|
||||
const bool version_satisfied = !reserved &&
|
||||
(operand_min_version <= module_version) &&
|
||||
(module_version <= operand_last_version);
|
||||
|
||||
if (version_satisfied) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (operand_last_version < module_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(which_operand)
|
||||
<< " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
|
||||
<< operand_desc.name << "(" << word << ") requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(operand_last_version)
|
||||
<< " or earlier";
|
||||
}
|
||||
|
||||
if (!reserved && operand_desc.numExtensions == 0) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(which_operand)
|
||||
<< " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
|
||||
<< operand_desc.name << "(" << word << ") requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later";
|
||||
} else {
|
||||
ExtensionSet required_extensions(operand_desc.numExtensions,
|
||||
operand_desc.extensions);
|
||||
if (!_.HasAnyOfExtensions(required_extensions)) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(which_operand)
|
||||
<< " operand of " << spvOpcodeString(inst->opcode())
|
||||
<< ": operand " << operand_desc.name << "(" << word
|
||||
<< ") requires one of these extensions: "
|
||||
<< ExtensionSetToString(required_extensions);
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns SPV_SUCCESS if the given operand is enabled by capabilities declared
|
||||
// in the module. Otherwise issues an error message and returns
|
||||
// SPV_ERROR_INVALID_CAPABILITY.
|
||||
spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
||||
const Instruction* inst,
|
||||
size_t which_operand,
|
||||
spv_operand_type_t type,
|
||||
uint32_t operand) {
|
||||
const spv_parsed_operand_t& operand,
|
||||
uint32_t word) {
|
||||
// Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin
|
||||
// decoration does not require the associated capability. The use of such
|
||||
// a variable value should trigger the capability requirement, but that's
|
||||
// not implemented yet. This rule is independent of target environment.
|
||||
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
|
||||
if (type == SPV_OPERAND_TYPE_BUILT_IN) {
|
||||
switch (operand) {
|
||||
if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) {
|
||||
switch (word) {
|
||||
case SpvBuiltInPointSize:
|
||||
case SpvBuiltInClipDistance:
|
||||
case SpvBuiltInCullDistance:
|
||||
@@ -120,14 +171,14 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) {
|
||||
} else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) {
|
||||
// Allow all FP rounding modes if requested
|
||||
if (state.features().free_fp_rounding_mode) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} else if (type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
|
||||
} else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
|
||||
state.features().group_ops_reduce_and_scans &&
|
||||
(operand <= uint32_t(SpvGroupOperationExclusiveScan))) {
|
||||
(word <= uint32_t(SpvGroupOperationExclusiveScan))) {
|
||||
// Allow certain group operations if requested.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@@ -135,10 +186,10 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
||||
CapabilitySet enabling_capabilities;
|
||||
spv_operand_desc operand_desc = nullptr;
|
||||
const auto lookup_result =
|
||||
state.grammar().lookupOperand(type, operand, &operand_desc);
|
||||
state.grammar().lookupOperand(operand.type, word, &operand_desc);
|
||||
if (lookup_result == SPV_SUCCESS) {
|
||||
// Allow FPRoundingMode decoration if requested.
|
||||
if (type == SPV_OPERAND_TYPE_DECORATION &&
|
||||
if (operand.type == SPV_OPERAND_TYPE_DECORATION &&
|
||||
operand_desc->value == SpvDecorationFPRoundingMode) {
|
||||
if (state.features().free_fp_rounding_mode) return SPV_SUCCESS;
|
||||
|
||||
@@ -161,29 +212,13 @@ spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
|
||||
<< " requires one of these capabilities: "
|
||||
<< ToString(enabling_capabilities, state.grammar());
|
||||
}
|
||||
return OperandVersionExtensionCheck(state, inst, which_operand,
|
||||
*operand_desc, word);
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns operand's required extensions.
|
||||
ExtensionSet RequiredExtensions(const ValidationState_t& state,
|
||||
spv_operand_type_t type, uint32_t operand) {
|
||||
spv_operand_desc operand_desc;
|
||||
if (state.grammar().lookupOperand(type, operand, &operand_desc) ==
|
||||
SPV_SUCCESS) {
|
||||
assert(operand_desc);
|
||||
// If this operand is incorporated into core SPIR-V before or in the current
|
||||
// target environment, we don't require extensions anymore.
|
||||
if (spvVersionForTargetEnv(state.grammar().target_env()) >=
|
||||
operand_desc->minVersion)
|
||||
return {};
|
||||
return {operand_desc->numExtensions, operand_desc->extensions};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
|
||||
// is explicitly reserved in the SPIR-V core spec. Otherwise return
|
||||
// SPV_SUCCESS.
|
||||
@@ -244,7 +279,7 @@ spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) {
|
||||
if (word & mask_bit) {
|
||||
spv_result_t status =
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand.type, mask_bit);
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit);
|
||||
if (status != SPV_SUCCESS) return status;
|
||||
}
|
||||
}
|
||||
@@ -255,34 +290,13 @@ spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
} else {
|
||||
// Check the operand word as a whole.
|
||||
spv_result_t status =
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand.type, word);
|
||||
CheckRequiredCapabilities(_, inst, i + 1, operand, word);
|
||||
if (status != SPV_SUCCESS) return status;
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that all extensions required by the given instruction's operands were
|
||||
// declared in the module.
|
||||
spv_result_t ExtensionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
const SpvOp opcode = inst->opcode();
|
||||
for (size_t operand_index = 0; operand_index < inst->operands().size();
|
||||
++operand_index) {
|
||||
const auto& operand = inst->operand(operand_index);
|
||||
const uint32_t word = inst->word(operand.offset);
|
||||
const ExtensionSet required_extensions =
|
||||
RequiredExtensions(_, operand.type, word);
|
||||
if (!_.HasAnyOfExtensions(required_extensions)) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvtools::utils::CardinalToOrdinal(operand_index + 1)
|
||||
<< " operand of " << spvOpcodeString(opcode) << ": operand "
|
||||
<< word << " requires one of these extensions: "
|
||||
<< ExtensionSetToString(required_extensions);
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that the instruction can be used in this target environment's base
|
||||
// version. Assumes that CapabilityCheck has checked direct capability
|
||||
// dependencies for the opcode.
|
||||
@@ -294,6 +308,15 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
(void)r;
|
||||
|
||||
const auto min_version = inst_desc->minVersion;
|
||||
const auto last_version = inst_desc->lastVersion;
|
||||
const auto module_version = _.version();
|
||||
|
||||
if (last_version < module_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(last_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier";
|
||||
}
|
||||
|
||||
if (inst_desc->numCapabilities > 0u) {
|
||||
// We already checked that the direct capability dependency has been
|
||||
@@ -303,22 +326,23 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions);
|
||||
if (exts.IsEmpty()) {
|
||||
// If no extensions can enable this instruction, then emit error messages
|
||||
// only concerning core SPIR-V versions if errors happen.
|
||||
// If no extensions can enable this instruction, then emit error
|
||||
// messages only concerning core SPIR-V versions if errors happen.
|
||||
if (min_version == ~0u) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " is reserved for future use.";
|
||||
}
|
||||
|
||||
if (spvVersionForTargetEnv(_.grammar().target_env()) < min_version) {
|
||||
if (module_version < min_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires "
|
||||
<< spvTargetEnvDescription(
|
||||
static_cast<spv_target_env>(min_version))
|
||||
<< " at minimum.";
|
||||
}
|
||||
// Otherwise, we only error out when no enabling extensions are registered.
|
||||
} else if (!_.HasAnyOfExtensions(exts)) {
|
||||
// Otherwise, we only error out when no enabling extensions are
|
||||
// registered.
|
||||
if (min_version == ~0u) {
|
||||
return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
@@ -326,11 +350,11 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
|
||||
<< ExtensionSetToString(exts);
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(_.grammar().target_env()) < min_version) {
|
||||
if (module_version < min_version) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< spvOpcodeString(opcode) << " requires "
|
||||
<< spvTargetEnvDescription(
|
||||
static_cast<spv_target_env>(min_version))
|
||||
<< spvOpcodeString(opcode) << " requires SPIR-V version "
|
||||
<< SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
|
||||
<< SPV_SPIRV_VERSION_MINOR_PART(min_version)
|
||||
<< " at minimum or one of the following extensions: "
|
||||
<< ExtensionSetToString(exts);
|
||||
}
|
||||
@@ -367,11 +391,10 @@ spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
// Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth"
|
||||
// must be less than or equal to 255.
|
||||
// This is interpreted as structures including other structures as members.
|
||||
// The code does not follow pointers or look into arrays to see if we reach a
|
||||
// structure downstream.
|
||||
// The nesting depth of a struct is 1+(largest depth of any member).
|
||||
// Scalars are at depth 0.
|
||||
// This is interpreted as structures including other structures as
|
||||
// members. The code does not follow pointers or look into arrays to see
|
||||
// if we reach a structure downstream. The nesting depth of a struct is
|
||||
// 1+(largest depth of any member). Scalars are at depth 0.
|
||||
uint32_t max_member_depth = 0;
|
||||
// Struct members start at word 2 of OpTypeStruct instruction.
|
||||
for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) {
|
||||
@@ -394,8 +417,8 @@ spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that the number of (literal, label) pairs in OpSwitch is within the
|
||||
// limit.
|
||||
// Checks that the number of (literal, label) pairs in OpSwitch is within
|
||||
// the limit.
|
||||
spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
|
||||
if (SpvOpSwitch == inst->opcode()) {
|
||||
// The instruction syntax is as follows:
|
||||
@@ -414,7 +437,8 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Ensure the number of variables of the given class does not exceed the limit.
|
||||
// Ensure the number of variables of the given class does not exceed the
|
||||
// limit.
|
||||
spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id,
|
||||
const SpvStorageClass storage_class) {
|
||||
if (SpvStorageClassFunction == storage_class) {
|
||||
@@ -472,7 +496,8 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (_.memory_model() != SpvMemoryModelVulkanKHR &&
|
||||
_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "VulkanMemoryModelKHR capability must only be specified if the "
|
||||
<< "VulkanMemoryModelKHR capability must only be specified if "
|
||||
"the "
|
||||
"VulkanKHR memory model is used.";
|
||||
}
|
||||
|
||||
@@ -486,6 +511,19 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "Memory model must be VulkanKHR for WebGPU environment.";
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsOpenCLEnv(_.context()->target_env)) {
|
||||
if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
|
||||
(_.addressing_model() != SpvAddressingModelPhysical64)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Addressing model must be Physical32 or Physical64 "
|
||||
<< "in the OpenCL environment.";
|
||||
}
|
||||
if (_.memory_model() != SpvMemoryModelOpenCL) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Memory model must be OpenCL in the OpenCL environment.";
|
||||
}
|
||||
}
|
||||
} else if (opcode == SpvOpExecutionMode) {
|
||||
const uint32_t entry_point = inst->word(1);
|
||||
_.RegisterExecutionModeForEntryPoint(entry_point,
|
||||
@@ -548,7 +586,6 @@ spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
"capability is used.";
|
||||
}
|
||||
|
||||
if (auto error = ExtensionCheck(_, inst)) return error;
|
||||
if (auto error = ReservedCheck(_, inst)) return error;
|
||||
if (auto error = EnvironmentCheck(_, inst)) return error;
|
||||
if (auto error = CapabilityCheck(_, inst)) return error;
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -27,10 +28,16 @@ namespace val {
|
||||
namespace {
|
||||
|
||||
// Returns true if \c inst is an input or output variable.
|
||||
bool is_interface_variable(const Instruction* inst) {
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
(inst->word(3u) == SpvStorageClassInput ||
|
||||
inst->word(3u) == SpvStorageClassOutput);
|
||||
bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
|
||||
if (is_spv_1_4) {
|
||||
// Starting in SPIR-V 1.4, all global variables are interface variables.
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
inst->word(3u) != SpvStorageClassFunction;
|
||||
} else {
|
||||
return inst->opcode() == SpvOpVariable &&
|
||||
(inst->word(3u) == SpvStorageClassInput ||
|
||||
inst->word(3u) == SpvStorageClassOutput);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that \c var is listed as an interface in all the entry points that use
|
||||
@@ -85,9 +92,8 @@ spv_result_t check_interface_variable(ValidationState_t& _,
|
||||
}
|
||||
if (!found) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, var)
|
||||
<< (var->word(3u) == SpvStorageClassInput ? "Input" : "Output")
|
||||
<< " variable id <" << var->id() << "> is used by entry point '"
|
||||
<< desc.name << "' id <" << id
|
||||
<< "Interface variable id <" << var->id()
|
||||
<< "> is used by entry point '" << desc.name << "' id <" << id
|
||||
<< ">, but is not listed as an interface";
|
||||
}
|
||||
}
|
||||
@@ -99,8 +105,9 @@ spv_result_t check_interface_variable(ValidationState_t& _,
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateInterfaces(ValidationState_t& _) {
|
||||
bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4);
|
||||
for (auto& inst : _.ordered_instructions()) {
|
||||
if (is_interface_variable(&inst)) {
|
||||
if (is_interface_variable(&inst, is_spv_1_4)) {
|
||||
if (auto error = check_interface_variable(_, &inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -151,6 +151,14 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
|
||||
const Instruction* type_inst = _.FindDef(result_type);
|
||||
assert(type_inst);
|
||||
|
||||
const auto composites = _.features().select_between_composites;
|
||||
auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected scalar or "
|
||||
<< (composites ? "composite" : "vector")
|
||||
<< " type as Result Type: " << spvOpcodeString(opcode);
|
||||
};
|
||||
|
||||
const SpvOp type_opcode = type_inst->opcode();
|
||||
switch (type_opcode) {
|
||||
case SpvOpTypePointer: {
|
||||
@@ -174,35 +182,48 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Not RuntimeArray because of other rules.
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeStruct: {
|
||||
if (!composites) return fail();
|
||||
break;
|
||||
};
|
||||
|
||||
default:
|
||||
return fail();
|
||||
}
|
||||
|
||||
const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
|
||||
const uint32_t left_type = _.GetOperandTypeId(inst, 3);
|
||||
const uint32_t right_type = _.GetOperandTypeId(inst, 4);
|
||||
|
||||
if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
|
||||
!_.IsBoolVectorType(condition_type)))
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected bool scalar or vector type as condition: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
if (_.GetDimension(condition_type) != dimension) {
|
||||
// If the condition is a vector type, then the result must also be a
|
||||
// vector with matching dimensions. In SPIR-V 1.4, a scalar condition
|
||||
// can be used to select between vector types. |composites| is a
|
||||
// proxy for SPIR-V 1.4 functionality.
|
||||
if (!composites || _.IsBoolVectorType(condition_type)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected scalar or vector type as Result Type: "
|
||||
<< "Expected vector sizes of Result Type and the condition "
|
||||
"to be equal: "
|
||||
<< spvOpcodeString(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
if (result_type != left_type || result_type != right_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected both objects to be of Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
|
||||
const uint32_t left_type = _.GetOperandTypeId(inst, 3);
|
||||
const uint32_t right_type = _.GetOperandTypeId(inst, 4);
|
||||
|
||||
if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
|
||||
!_.IsBoolVectorType(condition_type)))
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected bool scalar or vector type as condition: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
if (_.GetDimension(condition_type) != dimension)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected vector sizes of Result Type and the condition to be"
|
||||
<< " equal: " << spvOpcodeString(opcode);
|
||||
|
||||
if (result_type != left_type || result_type != right_type)
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected both objects to be of Result Type: "
|
||||
<< spvOpcodeString(opcode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SpvOpIEqual:
|
||||
|
||||
180
3rdparty/spirv-tools/source/val/validate_memory.cpp
vendored
180
3rdparty/spirv-tools/source/val/validate_memory.cpp
vendored
@@ -263,59 +263,40 @@ std::pair<SpvStorageClass, SpvStorageClass> GetStorageClass(
|
||||
return std::make_pair(dst_sc, src_sc);
|
||||
}
|
||||
|
||||
// Returns the number of instruction words taken up by a memory access
|
||||
// argument and its implied operands.
|
||||
int MemoryAccessNumWords(uint32_t mask) {
|
||||
int result = 1; // Count the mask
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
|
||||
if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the scope ID operand for MakeAvailable memory access with mask
|
||||
// at the given operand index.
|
||||
// This function is only called for OpLoad, OpStore, OpCopyMemory and
|
||||
// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
|
||||
// OpCooperativeMatrixStoreNV.
|
||||
uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask) {
|
||||
uint32_t offset = 1;
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++offset;
|
||||
|
||||
uint32_t scope_id = 0;
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpCopyMemorySized:
|
||||
return inst->GetOperandAs<uint32_t>(3 + offset);
|
||||
case SpvOpStore:
|
||||
case SpvOpCopyMemory:
|
||||
return inst->GetOperandAs<uint32_t>(2 + offset);
|
||||
case SpvOpCooperativeMatrixLoadNV:
|
||||
return inst->GetOperandAs<uint32_t>(5 + offset);
|
||||
case SpvOpCooperativeMatrixStoreNV:
|
||||
return inst->GetOperandAs<uint32_t>(4 + offset);
|
||||
default:
|
||||
assert(false && "unexpected opcode");
|
||||
break;
|
||||
}
|
||||
|
||||
return scope_id;
|
||||
uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask,
|
||||
uint32_t mask_index) {
|
||||
assert(mask & SpvMemoryAccessMakePointerAvailableKHRMask);
|
||||
uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerAvailableKHRMask);
|
||||
uint32_t index =
|
||||
mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
|
||||
return inst->GetOperandAs<uint32_t>(index);
|
||||
}
|
||||
|
||||
// This function is only called for OpLoad, OpStore, OpCopyMemory,
|
||||
// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
|
||||
// OpCooperativeMatrixStoreNV.
|
||||
uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask) {
|
||||
uint32_t offset = 1;
|
||||
if (mask & SpvMemoryAccessAlignedMask) ++offset;
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++offset;
|
||||
|
||||
uint32_t scope_id = 0;
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpCopyMemorySized:
|
||||
return inst->GetOperandAs<uint32_t>(3 + offset);
|
||||
case SpvOpStore:
|
||||
case SpvOpCopyMemory:
|
||||
return inst->GetOperandAs<uint32_t>(2 + offset);
|
||||
case SpvOpCooperativeMatrixLoadNV:
|
||||
return inst->GetOperandAs<uint32_t>(5 + offset);
|
||||
case SpvOpCooperativeMatrixStoreNV:
|
||||
return inst->GetOperandAs<uint32_t>(4 + offset);
|
||||
default:
|
||||
assert(false && "unexpected opcode");
|
||||
break;
|
||||
}
|
||||
|
||||
return scope_id;
|
||||
uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask,
|
||||
uint32_t mask_index) {
|
||||
assert(mask & SpvMemoryAccessMakePointerVisibleKHRMask);
|
||||
uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerVisibleKHRMask);
|
||||
uint32_t index =
|
||||
mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1)));
|
||||
return inst->GetOperandAs<uint32_t>(index);
|
||||
}
|
||||
|
||||
bool DoesStructContainRTA(const ValidationState_t& _, const Instruction* inst) {
|
||||
@@ -342,7 +323,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t mask = inst->GetOperandAs<uint32_t>(index);
|
||||
const uint32_t mask = inst->GetOperandAs<uint32_t>(index);
|
||||
if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) {
|
||||
if (inst->opcode() == SpvOpLoad ||
|
||||
inst->opcode() == SpvOpCooperativeMatrixLoadNV) {
|
||||
@@ -357,7 +338,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
}
|
||||
|
||||
// Check the associated scope for MakeAvailableKHR.
|
||||
const auto available_scope = GetMakeAvailableScope(inst, mask);
|
||||
const auto available_scope = GetMakeAvailableScope(inst, mask, index);
|
||||
if (auto error = ValidateMemoryScope(_, inst, available_scope))
|
||||
return error;
|
||||
}
|
||||
@@ -376,7 +357,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
}
|
||||
|
||||
// Check the associated scope for MakeVisibleKHR.
|
||||
const auto visible_scope = GetMakeVisibleScope(inst, mask);
|
||||
const auto visible_scope = GetMakeVisibleScope(inst, mask, index);
|
||||
if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
|
||||
}
|
||||
|
||||
@@ -863,6 +844,51 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
assert(inst->opcode() == SpvOpCopyMemory ||
|
||||
inst->opcode() == SpvOpCopyMemorySized);
|
||||
const uint32_t first_access_index = inst->opcode() == SpvOpCopyMemory ? 2 : 3;
|
||||
if (inst->operands().size() > first_access_index) {
|
||||
if (auto error = CheckMemoryAccess(_, inst, first_access_index))
|
||||
return error;
|
||||
|
||||
const auto first_access = inst->GetOperandAs<uint32_t>(first_access_index);
|
||||
const uint32_t second_access_index =
|
||||
first_access_index + MemoryAccessNumWords(first_access);
|
||||
if (inst->operands().size() > second_access_index) {
|
||||
if (_.features().copy_memory_permits_two_memory_accesses) {
|
||||
if (auto error = CheckMemoryAccess(_, inst, second_access_index))
|
||||
return error;
|
||||
|
||||
// In the two-access form in SPIR-V 1.4 and later:
|
||||
// - the first is the target (write) access and it can't have
|
||||
// make-visible.
|
||||
// - the second is the source (read) access and it can't have
|
||||
// make-available.
|
||||
if (first_access & SpvMemoryAccessMakePointerVisibleKHRMask) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Target memory access must not include "
|
||||
"MakePointerVisibleKHR";
|
||||
}
|
||||
const auto second_access =
|
||||
inst->GetOperandAs<uint32_t>(second_access_index);
|
||||
if (second_access & SpvMemoryAccessMakePointerAvailableKHRMask) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Source memory access must not include "
|
||||
"MakePointerAvailableKHR";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(static_cast<SpvOp>(inst->opcode()))
|
||||
<< " with two memory access operands requires SPIR-V 1.4 or "
|
||||
"later";
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto target_index = 0;
|
||||
const auto target_id = inst->GetOperandAs<uint32_t>(target_index);
|
||||
@@ -968,7 +994,7 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
return ValidateCopyMemoryMemoryAccess(_, inst);
|
||||
}
|
||||
|
||||
spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
@@ -1326,6 +1352,57 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidatePtrComparison(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (_.addressing_model() == SpvAddressingModelLogical &&
|
||||
!_.features().variable_pointers_storage_buffer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Instruction cannot be used without a variable pointers "
|
||||
"capability";
|
||||
}
|
||||
|
||||
const auto result_type = _.FindDef(inst->type_id());
|
||||
if (inst->opcode() == SpvOpPtrDiff) {
|
||||
if (!result_type || result_type->opcode() != SpvOpTypeInt) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must be an integer scalar";
|
||||
}
|
||||
} else {
|
||||
if (!result_type || result_type->opcode() != SpvOpTypeBool) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result Type must be OpTypeBool";
|
||||
}
|
||||
}
|
||||
|
||||
const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
|
||||
const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
|
||||
if (!op1 || !op2 || op1->type_id() != op2->type_id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The types of Operand 1 and Operand 2 must match";
|
||||
}
|
||||
const auto op1_type = _.FindDef(op1->type_id());
|
||||
if (!op1_type || op1_type->opcode() != SpvOpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand type must be a pointer";
|
||||
}
|
||||
|
||||
if (_.addressing_model() == SpvAddressingModelLogical) {
|
||||
SpvStorageClass sc = op1_type->GetOperandAs<SpvStorageClass>(1u);
|
||||
if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Invalid pointer storage class";
|
||||
}
|
||||
|
||||
if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Workgroup storage class pointer requires VariablePointers "
|
||||
"capability to be specified";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
@@ -1362,6 +1439,11 @@ spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
case SpvOpCooperativeMatrixLengthNV:
|
||||
if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpPtrEqual:
|
||||
case SpvOpPtrNotEqual:
|
||||
case SpvOpPtrDiff:
|
||||
if (auto error = ValidatePtrComparison(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpImageTexelPointer:
|
||||
case SpvOpGenericPtrMemSemantics:
|
||||
default:
|
||||
|
||||
@@ -22,6 +22,23 @@
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
|
||||
bool IsValidScope(uint32_t scope) {
|
||||
// Deliberately avoid a default case so we have to update the list when the
|
||||
// scopes list changes.
|
||||
switch (static_cast<SpvScope>(scope)) {
|
||||
case SpvScopeCrossDevice:
|
||||
case SpvScopeDevice:
|
||||
case SpvScopeWorkgroup:
|
||||
case SpvScopeSubgroup:
|
||||
case SpvScopeInvocation:
|
||||
case SpvScopeQueueFamilyKHR:
|
||||
return true;
|
||||
case SpvScopeMax:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
||||
const Instruction* inst, uint32_t scope) {
|
||||
SpvOp opcode = inst->opcode();
|
||||
@@ -52,6 +69,11 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (is_const_int32 && !IsValidScope(value)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
|
||||
}
|
||||
|
||||
// Vulkan specific rules
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
// Vulkan 1.1 specific rules
|
||||
@@ -154,6 +176,11 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (is_const_int32 && !IsValidScope(value)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
|
||||
}
|
||||
|
||||
if (value == SpvScopeQueueFamilyKHR) {
|
||||
if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
|
||||
return SPV_SUCCESS;
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
|
||||
// Ensures type declarations are unique unless allowed by the specification.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@@ -67,6 +66,16 @@ spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto signedness_index = 2;
|
||||
const auto signedness = inst->GetOperandAs<uint32_t>(signedness_index);
|
||||
if (signedness != 0 && signedness != 1) {
|
||||
return _.diag(SPV_ERROR_INVALID_VALUE, inst)
|
||||
<< "OpTypeInt has invalid signedness:";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto component_index = 1;
|
||||
const auto component_id = inst->GetOperandAs<uint32_t>(component_index);
|
||||
@@ -310,6 +319,7 @@ spv_result_t ValidateTypePointer(ValidationState_t& _,
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateTypeFunction(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
@@ -428,8 +438,6 @@ spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!spvOpcodeGeneratesType(inst->opcode()) &&
|
||||
inst->opcode() != SpvOpTypeForwardPointer) {
|
||||
@@ -439,6 +447,9 @@ spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (auto error = ValidateUniqueness(_, inst)) return error;
|
||||
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpTypeInt:
|
||||
if (auto error = ValidateTypeInt(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
if (auto error = ValidateTypeVector(_, inst)) return error;
|
||||
break;
|
||||
|
||||
104
3rdparty/spirv-tools/source/val/validation_state.cpp
vendored
104
3rdparty/spirv-tools/source/val/validation_state.cpp
vendored
@@ -19,6 +19,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/basic_block.h"
|
||||
#include "source/val/construct.h"
|
||||
@@ -146,6 +147,30 @@ spv_result_t CountInstructions(void* user_data,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t) {
|
||||
ValidationState_t& vstate =
|
||||
*(reinterpret_cast<ValidationState_t*>(user_data));
|
||||
vstate.setIdBound(id_bound);
|
||||
vstate.setGenerator(generator);
|
||||
vstate.setVersion(version);
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Add features based on SPIR-V core version number.
|
||||
void UpdateFeaturesBasedOnSpirvVersion(ValidationState_t::Feature* features,
|
||||
uint32_t version) {
|
||||
assert(features);
|
||||
if (version >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
features->select_between_composites = true;
|
||||
features->copy_memory_permits_two_memory_accesses = true;
|
||||
features->uconvert_spec_constant_op = true;
|
||||
features->nonwritable_var_in_function_or_private = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ValidationState_t::ValidationState_t(const spv_const_context ctx,
|
||||
@@ -205,11 +230,12 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx,
|
||||
spv_context_t hijacked_context = *ctx;
|
||||
hijacked_context.consumer = [](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char*) {};
|
||||
spvBinaryParse(&hijacked_context, this, words, num_words,
|
||||
/* parsed_header = */ nullptr, CountInstructions,
|
||||
spvBinaryParse(&hijacked_context, this, words, num_words, setHeader,
|
||||
CountInstructions,
|
||||
/* diagnostic = */ nullptr);
|
||||
preallocateStorage();
|
||||
}
|
||||
UpdateFeaturesBasedOnSpirvVersion(&features_, version_);
|
||||
|
||||
friendly_mapper_ = spvtools::MakeUnique<spvtools::FriendlyNameMapper>(
|
||||
context_, words_, num_words_);
|
||||
@@ -447,7 +473,7 @@ void ValidationState_t::set_addressing_model(SpvAddressingModel am) {
|
||||
pointer_size_and_alignment_ = 4;
|
||||
break;
|
||||
default:
|
||||
// fall through
|
||||
// fall through
|
||||
case SpvAddressingModelPhysical64:
|
||||
case SpvAddressingModelPhysicalStorageBuffer64EXT:
|
||||
pointer_size_and_alignment_ = 8;
|
||||
@@ -1109,5 +1135,77 @@ std::string ValidationState_t::Disassemble(const uint32_t* words,
|
||||
words_, num_words_, disassembly_options);
|
||||
}
|
||||
|
||||
bool ValidationState_t::LogicallyMatch(const Instruction* lhs,
|
||||
const Instruction* rhs,
|
||||
bool check_decorations) {
|
||||
if (lhs->opcode() != rhs->opcode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (check_decorations) {
|
||||
const auto& dec_a = id_decorations(lhs->id());
|
||||
const auto& dec_b = id_decorations(rhs->id());
|
||||
|
||||
for (const auto& dec : dec_b) {
|
||||
if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lhs->opcode() == SpvOpTypeArray) {
|
||||
// Size operands must match.
|
||||
if (lhs->GetOperandAs<uint32_t>(2u) != rhs->GetOperandAs<uint32_t>(2u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Elements must match or logically match.
|
||||
const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(1u);
|
||||
const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(1u);
|
||||
if (lhs_ele_id == rhs_ele_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto lhs_ele = FindDef(lhs_ele_id);
|
||||
const auto rhs_ele = FindDef(rhs_ele_id);
|
||||
if (!lhs_ele || !rhs_ele) {
|
||||
return false;
|
||||
}
|
||||
return LogicallyMatch(lhs_ele, rhs_ele, check_decorations);
|
||||
} else if (lhs->opcode() == SpvOpTypeStruct) {
|
||||
// Number of elements must match.
|
||||
if (lhs->operands().size() != rhs->operands().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 1u; i < lhs->operands().size(); ++i) {
|
||||
const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(i);
|
||||
const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(i);
|
||||
// Elements must match or logically match.
|
||||
if (lhs_ele_id == rhs_ele_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto lhs_ele = FindDef(lhs_ele_id);
|
||||
const auto rhs_ele = FindDef(rhs_ele_id);
|
||||
if (!lhs_ele || !rhs_ele) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LogicallyMatch(lhs_ele, rhs_ele, check_decorations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All checks passed.
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other opcodes are acceptable at this point. Arrays and structs are
|
||||
// caught above and if they're elements are not arrays or structs they are
|
||||
// required to match exactly.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -106,9 +106,20 @@ class ValidationState_t {
|
||||
// Members need not be listed in offset order
|
||||
bool scalar_block_layout = false;
|
||||
|
||||
// Permit UConvert as an OpSpecConstantOp operation.
|
||||
// SPIR-V 1.4 allows us to select between any two composite values
|
||||
// of the same type.
|
||||
bool select_between_composites = false;
|
||||
|
||||
// SPIR-V 1.4 allows two memory access operands for OpCopyMemory and
|
||||
// OpCopyMemorySized.
|
||||
bool copy_memory_permits_two_memory_accesses = false;
|
||||
|
||||
// SPIR-V 1.4 allows UConvert as a spec constant op in any environment.
|
||||
// The Kernel capability already enables it, separately from this flag.
|
||||
bool uconvert_spec_constant_op = false;
|
||||
|
||||
// SPIR-V 1.4 allows Function and Private variables to be NonWritable
|
||||
bool nonwritable_var_in_function_or_private = false;
|
||||
};
|
||||
|
||||
ValidationState_t(const spv_const_context context,
|
||||
@@ -653,6 +664,21 @@ class ValidationState_t {
|
||||
spv_result_t CooperativeMatrixShapesMatch(const Instruction* inst,
|
||||
uint32_t m1, uint32_t m2);
|
||||
|
||||
// Returns true if |lhs| and |rhs| logically match and, if the decorations of
|
||||
// |rhs| are a subset of |lhs|.
|
||||
//
|
||||
// 1. Must both be either OpTypeArray or OpTypeStruct
|
||||
// 2. If OpTypeArray, then
|
||||
// * Length must be the same
|
||||
// * Element type must match or logically match
|
||||
// 3. If OpTypeStruct, then
|
||||
// * Both have same number of elements
|
||||
// * Element N for both structs must match or logically match
|
||||
//
|
||||
// If |check_decorations| is false, then the decorations are not checked.
|
||||
bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs,
|
||||
bool check_decorations);
|
||||
|
||||
private:
|
||||
ValidationState_t(const ValidationState_t&);
|
||||
|
||||
|
||||
1
3rdparty/spirv-tools/test/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/test/CMakeLists.txt
vendored
@@ -129,6 +129,7 @@ set(TEST_SOURCES
|
||||
text_start_new_inst_test.cpp
|
||||
text_to_binary.annotation_test.cpp
|
||||
text_to_binary.barrier_test.cpp
|
||||
text_to_binary.composite_test.cpp
|
||||
text_to_binary.constant_test.cpp
|
||||
text_to_binary.control_flow_test.cpp
|
||||
text_to_binary_test.cpp
|
||||
|
||||
@@ -51,7 +51,7 @@ TEST_F(BinaryHeaderGet, Default) {
|
||||
ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
|
||||
|
||||
ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
|
||||
ASSERT_EQ(0x00010300u, header.version);
|
||||
ASSERT_EQ(0x00010400u, header.version);
|
||||
ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
|
||||
ASSERT_EQ(1u, header.bound);
|
||||
ASSERT_EQ(0u, header.schema);
|
||||
|
||||
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
@@ -81,6 +81,7 @@ add_spvtools_unittest(TARGET opt
|
||||
scalar_replacement_test.cpp
|
||||
set_spec_const_default_value_test.cpp
|
||||
simplification_test.cpp
|
||||
split_invalid_unreachable_test.cpp
|
||||
strength_reduction_test.cpp
|
||||
strip_atomic_counter_memory_test.cpp
|
||||
strip_debug_info_test.cpp
|
||||
|
||||
@@ -6389,6 +6389,113 @@ OpFunctionEnd
|
||||
SinglePassRunAndCheck<AggressiveDCEPass>(before, before, true, true);
|
||||
}
|
||||
|
||||
TEST_F(AggressiveDCETest, DeadInputInterfaceV13) {
|
||||
const std::string spirv = R"(
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]]
|
||||
; CHECK: [[var]] = OpVariable
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %dead
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_input_int = OpTypePointer Input %int
|
||||
%dead = OpVariable %ptr_input_int Input
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
|
||||
SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
|
||||
}
|
||||
|
||||
TEST_F(AggressiveDCETest, DeadInputInterfaceV14) {
|
||||
const std::string spirv = R"(
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]]
|
||||
; CHECK: [[var]] = OpVariable
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %dead
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_input_int = OpTypePointer Input %int
|
||||
%dead = OpVariable %ptr_input_int Input
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
|
||||
}
|
||||
|
||||
TEST_F(AggressiveDCETest, DeadInterfaceV14) {
|
||||
const std::string spirv = R"(
|
||||
; CHECK-NOT: OpEntryPoint GLCompute %main "main" %
|
||||
; CHECK: OpEntryPoint GLCompute %main "main"
|
||||
; CHECK-NOT: OpVariable
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %dead
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_private_int = OpTypePointer Private %int
|
||||
%dead = OpVariable %ptr_private_int Private
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
|
||||
}
|
||||
|
||||
TEST_F(AggressiveDCETest, DeadInterfacesV14) {
|
||||
const std::string spirv = R"(
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" %live1 %live2
|
||||
; CHECK-NOT: %dead
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %live1 %dead1 %dead2 %live2
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
OpName %live1 "live1"
|
||||
OpName %live2 "live2"
|
||||
OpName %dead1 "dead1"
|
||||
OpName %dead2 "dead2"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int0 = OpConstant %int 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%live1 = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%live2 = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dead1 = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dead2 = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpStore %live1 %int0
|
||||
OpStore %live2 %int0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
|
||||
}
|
||||
|
||||
// TODO(greg-lunarg): Add tests to verify handling of these cases:
|
||||
//
|
||||
// Check that logical addressing required
|
||||
|
||||
@@ -709,8 +709,8 @@ OpDecorate %1 Aliased
|
||||
EXPECT_THAT(GetErrorMessage(), "");
|
||||
|
||||
std::string expected_decorations =
|
||||
R"(OpDecorateStringGOOGLE %5 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %5 HlslCounterBufferGOOGLE %2
|
||||
R"(OpDecorateString %5 UserSemantic "blah"
|
||||
OpDecorateId %5 CounterBuffer %2
|
||||
OpDecorate %5 Aliased
|
||||
)";
|
||||
EXPECT_THAT(ToText(decorations), expected_decorations);
|
||||
@@ -720,11 +720,11 @@ OpCapability Linkage
|
||||
OpExtension "SPV_GOOGLE_hlsl_functionality1"
|
||||
OpExtension "SPV_GOOGLE_decorate_string"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %1 HlslCounterBufferGOOGLE %2
|
||||
OpDecorateString %1 UserSemantic "blah"
|
||||
OpDecorateId %1 CounterBuffer %2
|
||||
OpDecorate %1 Aliased
|
||||
OpDecorateStringGOOGLE %5 HlslSemanticGOOGLE "blah"
|
||||
OpDecorateId %5 HlslCounterBufferGOOGLE %2
|
||||
OpDecorateString %5 UserSemantic "blah"
|
||||
OpDecorateId %5 CounterBuffer %2
|
||||
OpDecorate %5 Aliased
|
||||
%3 = OpTypeInt 32 0
|
||||
%4 = OpTypePointer Uniform %3
|
||||
|
||||
175
3rdparty/spirv-tools/test/opt/fold_test.cpp
vendored
175
3rdparty/spirv-tools/test/opt/fold_test.cpp
vendored
@@ -6311,6 +6311,181 @@ INSTANTIATE_TEST_SUITE_P(OpEntryPointFoldingTest, EntryPointFoldingTest,
|
||||
9, true)
|
||||
));
|
||||
|
||||
using SPV14FoldingTest =
|
||||
::testing::TestWithParam<InstructionFoldingCase<bool>>;
|
||||
|
||||
TEST_P(SPV14FoldingTest, Case) {
|
||||
const auto& tc = GetParam();
|
||||
|
||||
// Build module.
|
||||
std::unique_ptr<IRContext> context =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body,
|
||||
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
ASSERT_NE(nullptr, context);
|
||||
|
||||
// Fold the instruction to test.
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
|
||||
std::unique_ptr<Instruction> original_inst(inst->Clone(context.get()));
|
||||
bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
|
||||
EXPECT_EQ(succeeded, tc.expected_result);
|
||||
if (succeeded) {
|
||||
Match(tc.test_body, context.get());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(SPV14FoldingTest, SPV14FoldingTest,
|
||||
::testing::Values(
|
||||
// Test case 0: select vectors with scalar condition.
|
||||
InstructionFoldingCase<bool>(std::string() +
|
||||
"; CHECK-NOT: OpSelect\n" +
|
||||
"; CHECK: %3 = OpCopyObject {{%\\w+}} %1\n" +
|
||||
"OpCapability Shader\n" +
|
||||
"OpCapability Linkage\n" +
|
||||
"%void = OpTypeVoid\n" +
|
||||
"%bool = OpTypeBool\n" +
|
||||
"%true = OpConstantTrue %bool\n" +
|
||||
"%int = OpTypeInt 32 0\n" +
|
||||
"%int4 = OpTypeVector %int 4\n" +
|
||||
"%int_0 = OpConstant %int 0\n" +
|
||||
"%int_1 = OpConstant %int 1\n" +
|
||||
"%1 = OpUndef %int4\n" +
|
||||
"%2 = OpUndef %int4\n" +
|
||||
"%void_fn = OpTypeFunction %void\n" +
|
||||
"%func = OpFunction %void None %void_fn\n" +
|
||||
"%entry = OpLabel\n" +
|
||||
"%3 = OpSelect %int4 %true %1 %2\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
3, true),
|
||||
// Test case 1: select struct with scalar condition.
|
||||
InstructionFoldingCase<bool>(std::string() +
|
||||
"; CHECK-NOT: OpSelect\n" +
|
||||
"; CHECK: %3 = OpCopyObject {{%\\w+}} %2\n" +
|
||||
"OpCapability Shader\n" +
|
||||
"OpCapability Linkage\n" +
|
||||
"%void = OpTypeVoid\n" +
|
||||
"%bool = OpTypeBool\n" +
|
||||
"%true = OpConstantFalse %bool\n" +
|
||||
"%int = OpTypeInt 32 0\n" +
|
||||
"%struct = OpTypeStruct %int %int %int %int\n" +
|
||||
"%int_0 = OpConstant %int 0\n" +
|
||||
"%int_1 = OpConstant %int 1\n" +
|
||||
"%1 = OpUndef %struct\n" +
|
||||
"%2 = OpUndef %struct\n" +
|
||||
"%void_fn = OpTypeFunction %void\n" +
|
||||
"%func = OpFunction %void None %void_fn\n" +
|
||||
"%entry = OpLabel\n" +
|
||||
"%3 = OpSelect %struct %true %1 %2\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
3, true),
|
||||
// Test case 1: select array with scalar condition.
|
||||
InstructionFoldingCase<bool>(std::string() +
|
||||
"; CHECK-NOT: OpSelect\n" +
|
||||
"; CHECK: %3 = OpCopyObject {{%\\w+}} %2\n" +
|
||||
"OpCapability Shader\n" +
|
||||
"OpCapability Linkage\n" +
|
||||
"%void = OpTypeVoid\n" +
|
||||
"%bool = OpTypeBool\n" +
|
||||
"%true = OpConstantFalse %bool\n" +
|
||||
"%int = OpTypeInt 32 0\n" +
|
||||
"%int_0 = OpConstant %int 0\n" +
|
||||
"%int_1 = OpConstant %int 1\n" +
|
||||
"%int_4 = OpConstant %int 4\n" +
|
||||
"%array = OpTypeStruct %int %int %int %int\n" +
|
||||
"%1 = OpUndef %array\n" +
|
||||
"%2 = OpUndef %array\n" +
|
||||
"%void_fn = OpTypeFunction %void\n" +
|
||||
"%func = OpFunction %void None %void_fn\n" +
|
||||
"%entry = OpLabel\n" +
|
||||
"%3 = OpSelect %array %true %1 %2\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
3, true)
|
||||
));
|
||||
|
||||
std::string FloatControlsHeader(const std::string& capabilities) {
|
||||
std::string header = R"(
|
||||
OpCapability Shader
|
||||
)" + capabilities + R"(
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%float_0 = OpConstant %float 0
|
||||
%float_1 = OpConstant %float 1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
)";
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
using FloatControlsFoldingTest =
|
||||
::testing::TestWithParam<InstructionFoldingCase<bool>>;
|
||||
|
||||
TEST_P(FloatControlsFoldingTest, Case) {
|
||||
const auto& tc = GetParam();
|
||||
|
||||
// Build module.
|
||||
std::unique_ptr<IRContext> context =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body,
|
||||
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
ASSERT_NE(nullptr, context);
|
||||
|
||||
// Fold the instruction to test.
|
||||
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
|
||||
Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold);
|
||||
std::unique_ptr<Instruction> original_inst(inst->Clone(context.get()));
|
||||
bool succeeded = context->get_instruction_folder().FoldInstruction(inst);
|
||||
EXPECT_EQ(succeeded, tc.expected_result);
|
||||
if (succeeded) {
|
||||
Match(tc.test_body, context.get());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FloatControlsFoldingTest, FloatControlsFoldingTest,
|
||||
::testing::Values(
|
||||
// Test case 0: no folding with DenormPreserve
|
||||
InstructionFoldingCase<bool>(FloatControlsHeader("OpCapability DenormPreserve") +
|
||||
"%1 = OpFAdd %float %float_0 %float_1\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
1, false),
|
||||
// Test case 1: no folding with DenormFlushToZero
|
||||
InstructionFoldingCase<bool>(FloatControlsHeader("OpCapability DenormFlushToZero") +
|
||||
"%1 = OpFAdd %float %float_0 %float_1\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
1, false),
|
||||
// Test case 2: no folding with SignedZeroInfNanPreserve
|
||||
InstructionFoldingCase<bool>(FloatControlsHeader("OpCapability SignedZeroInfNanPreserve") +
|
||||
"%1 = OpFAdd %float %float_0 %float_1\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
1, false),
|
||||
// Test case 3: no folding with RoundingModeRTE
|
||||
InstructionFoldingCase<bool>(FloatControlsHeader("OpCapability RoundingModeRTE") +
|
||||
"%1 = OpFAdd %float %float_0 %float_1\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
1, false),
|
||||
// Test case 4: no folding with RoundingModeRTZ
|
||||
InstructionFoldingCase<bool>(FloatControlsHeader("OpCapability RoundingModeRTZ") +
|
||||
"%1 = OpFAdd %float %float_0 %float_1\n" +
|
||||
"OpReturn\n" +
|
||||
"OpFunctionEnd\n"
|
||||
,
|
||||
1, false)
|
||||
));
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
72
3rdparty/spirv-tools/test/opt/optimizer_test.cpp
vendored
72
3rdparty/spirv-tools/test/opt/optimizer_test.cpp
vendored
@@ -238,7 +238,8 @@ TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) {
|
||||
"strip-debug",
|
||||
"strip-atomic-counter-memory",
|
||||
"generate-webgpu-initializers",
|
||||
"legalize-vector-shuffle"};
|
||||
"legalize-vector-shuffle",
|
||||
"split-invalid-unreachable"};
|
||||
std::sort(registered_passes.begin(), registered_passes.end());
|
||||
std::sort(expected_passes.begin(), expected_passes.end());
|
||||
|
||||
@@ -524,7 +525,74 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"legalize-vector-shuffle"}}));
|
||||
"legalize-vector-shuffle"},
|
||||
// Split Invalid Unreachable
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%uint_1 = OpConstant %uint 1\n"
|
||||
"%uint_2 = OpConstant %uint 2\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%bool = OpTypeBool\n"
|
||||
"%7 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %7\n"
|
||||
"%8 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpLoopMerge %10 %11 None\n"
|
||||
"OpBranch %12\n"
|
||||
"%12 = OpLabel\n"
|
||||
"%13 = OpSLessThan %bool %uint_1 %uint_2\n"
|
||||
"OpSelectionMerge %11 None\n"
|
||||
"OpBranchConditional %13 %14 %15\n"
|
||||
"%14 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%15 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%10 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%uint_1 = OpConstant %uint 1\n"
|
||||
"%uint_2 = OpConstant %uint 2\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%bool = OpTypeBool\n"
|
||||
"%7 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %7\n"
|
||||
"%8 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpLoopMerge %10 %11 None\n"
|
||||
"OpBranch %12\n"
|
||||
"%12 = OpLabel\n"
|
||||
"%13 = OpSLessThan %bool %uint_1 %uint_2\n"
|
||||
"OpSelectionMerge %16 None\n"
|
||||
"OpBranchConditional %13 %14 %15\n"
|
||||
"%14 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%15 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%10 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%16 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"split-invalid-unreachable"}}));
|
||||
|
||||
TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) {
|
||||
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||
|
||||
35
3rdparty/spirv-tools/test/opt/pass_fixture.h
vendored
35
3rdparty/spirv-tools/test/opt/pass_fixture.h
vendored
@@ -49,18 +49,18 @@ class PassTest : public TestT {
|
||||
[](spv_message_level_t, const char*, const spv_position_t&,
|
||||
const char* message) { std::cerr << message << std::endl; }),
|
||||
context_(nullptr),
|
||||
tools_(SPV_ENV_UNIVERSAL_1_3),
|
||||
manager_(new PassManager()),
|
||||
assemble_options_(SpirvTools::kDefaultAssembleOption),
|
||||
disassemble_options_(SpirvTools::kDefaultDisassembleOption) {}
|
||||
disassemble_options_(SpirvTools::kDefaultDisassembleOption),
|
||||
env_(SPV_ENV_UNIVERSAL_1_3) {}
|
||||
|
||||
// Runs the given |pass| on the binary assembled from the |original|.
|
||||
// Returns a tuple of the optimized binary and the boolean value returned
|
||||
// from pass Process() function.
|
||||
std::tuple<std::vector<uint32_t>, Pass::Status> OptimizeToBinary(
|
||||
Pass* pass, const std::string& original, bool skip_nop) {
|
||||
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer_, original,
|
||||
assemble_options_));
|
||||
context_ =
|
||||
std::move(BuildModule(env_, consumer_, original, assemble_options_));
|
||||
EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
|
||||
<< original << std::endl;
|
||||
if (!context()) {
|
||||
@@ -97,8 +97,7 @@ class PassTest : public TestT {
|
||||
std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
|
||||
assembly, skip_nop, std::forward<Args>(args)...);
|
||||
if (do_validation) {
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_3;
|
||||
spv_context spvContext = spvContextCreate(target_env);
|
||||
spv_context spvContext = spvContextCreate(env_);
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
|
||||
spv_result_t error = spvValidateWithOptions(
|
||||
@@ -109,8 +108,9 @@ class PassTest : public TestT {
|
||||
spvContextDestroy(spvContext);
|
||||
}
|
||||
std::string optimized_asm;
|
||||
SpirvTools tools(env_);
|
||||
EXPECT_TRUE(
|
||||
tools_.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
|
||||
tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
|
||||
<< "Disassembling failed for shader:\n"
|
||||
<< assembly << std::endl;
|
||||
return std::make_tuple(optimized_asm, status);
|
||||
@@ -134,8 +134,7 @@ class PassTest : public TestT {
|
||||
EXPECT_EQ(original == expected,
|
||||
status == Pass::Status::SuccessWithoutChange);
|
||||
if (do_validation) {
|
||||
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_3;
|
||||
spv_context spvContext = spvContextCreate(target_env);
|
||||
spv_context spvContext = spvContextCreate(env_);
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
|
||||
spv_result_t error = spvValidateWithOptions(
|
||||
@@ -146,8 +145,9 @@ class PassTest : public TestT {
|
||||
spvContextDestroy(spvContext);
|
||||
}
|
||||
std::string optimized_asm;
|
||||
SpirvTools tools(env_);
|
||||
EXPECT_TRUE(
|
||||
tools_.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
|
||||
tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
|
||||
<< "Disassembling failed for shader:\n"
|
||||
<< original << std::endl;
|
||||
EXPECT_EQ(expected, optimized_asm);
|
||||
@@ -202,8 +202,8 @@ class PassTest : public TestT {
|
||||
void RunAndCheck(const std::string& original, const std::string& expected) {
|
||||
assert(manager_->NumPasses());
|
||||
|
||||
context_ = std::move(BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, original,
|
||||
assemble_options_));
|
||||
context_ =
|
||||
std::move(BuildModule(env_, nullptr, original, assemble_options_));
|
||||
ASSERT_NE(nullptr, context());
|
||||
|
||||
manager_->Run(context());
|
||||
@@ -212,7 +212,8 @@ class PassTest : public TestT {
|
||||
context()->module()->ToBinary(&binary, /* skip_nop = */ false);
|
||||
|
||||
std::string optimized;
|
||||
EXPECT_TRUE(tools_.Disassemble(binary, &optimized, disassemble_options_));
|
||||
SpirvTools tools(env_);
|
||||
EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_));
|
||||
EXPECT_EQ(expected, optimized);
|
||||
}
|
||||
|
||||
@@ -233,14 +234,16 @@ class PassTest : public TestT {
|
||||
|
||||
spv_validator_options ValidatorOptions() { return &validator_options_; }
|
||||
|
||||
void SetTargetEnv(spv_target_env env) { env_ = env; }
|
||||
|
||||
private:
|
||||
MessageConsumer consumer_; // Message consumer.
|
||||
std::unique_ptr<IRContext> context_; // IR context
|
||||
SpirvTools tools_; // An instance for calling SPIRV-Tools functionalities.
|
||||
MessageConsumer consumer_; // Message consumer.
|
||||
std::unique_ptr<IRContext> context_; // IR context
|
||||
std::unique_ptr<PassManager> manager_; // The pass manager.
|
||||
uint32_t assemble_options_;
|
||||
uint32_t disassemble_options_;
|
||||
spv_validator_options_t validator_options_;
|
||||
spv_target_env env_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
||||
@@ -308,6 +308,117 @@ TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct2) {
|
||||
SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
|
||||
}
|
||||
|
||||
TEST_F(PrivateToLocalTest, SPV14RemoveFromInterface) {
|
||||
const std::string text = R"(
|
||||
; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv
|
||||
; CHECK: OpEntryPoint GLCompute %foo "foo" %in
|
||||
; CHECK: %priv = OpVariable {{%\w+}} Function
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %foo "foo" %in %priv
|
||||
OpExecutionMode %foo LocalSize 1 1 1
|
||||
OpName %foo "foo"
|
||||
OpName %in "in"
|
||||
OpName %priv "priv"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%ptr_private_int = OpTypePointer Private %int
|
||||
%in = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%priv = OpVariable %ptr_private_int Private
|
||||
%void_fn = OpTypeFunction %void
|
||||
%foo = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%ld = OpLoad %int %in
|
||||
OpStore %priv %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleEntryPoints) {
|
||||
const std::string text = R"(
|
||||
; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv
|
||||
; CHECK-NOT: OpEntryPoint GLCompute %foo "bar" %in %priv
|
||||
; CHECK: OpEntryPoint GLCompute %foo "foo" %in
|
||||
; CHECK: OpEntryPoint GLCompute %foo "bar" %in
|
||||
; CHECK: %priv = OpVariable {{%\w+}} Function
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %foo "foo" %in %priv
|
||||
OpEntryPoint GLCompute %foo "bar" %in %priv
|
||||
OpExecutionMode %foo LocalSize 1 1 1
|
||||
OpName %foo "foo"
|
||||
OpName %in "in"
|
||||
OpName %priv "priv"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%ptr_private_int = OpTypePointer Private %int
|
||||
%in = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%priv = OpVariable %ptr_private_int Private
|
||||
%void_fn = OpTypeFunction %void
|
||||
%foo = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%ld = OpLoad %int %in
|
||||
OpStore %priv %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleVariables) {
|
||||
const std::string text = R"(
|
||||
; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2
|
||||
; CHECK: OpEntryPoint GLCompute %foo "foo" %in
|
||||
; CHECK: %priv1 = OpVariable {{%\w+}} Function
|
||||
; CHECK: %priv2 = OpVariable {{%\w+}} Function
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2
|
||||
OpExecutionMode %foo LocalSize 1 1 1
|
||||
OpName %foo "foo"
|
||||
OpName %in "in"
|
||||
OpName %priv1 "priv1"
|
||||
OpName %priv2 "priv2"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%ptr_private_int = OpTypePointer Private %int
|
||||
%in = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%priv1 = OpVariable %ptr_private_int Private
|
||||
%priv2 = OpVariable %ptr_private_int Private
|
||||
%void_fn = OpTypeFunction %void
|
||||
%foo = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%1 = OpFunctionCall %void %bar1
|
||||
%2 = OpFunctionCall %void %bar2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%bar1 = OpFunction %void None %void_fn
|
||||
%3 = OpLabel
|
||||
%ld1 = OpLoad %int %in
|
||||
OpStore %priv1 %ld1
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%bar2 = OpFunction %void None %void_fn
|
||||
%4 = OpLabel
|
||||
%ld2 = OpLoad %int %in
|
||||
OpStore %priv2 %ld2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -1620,6 +1620,43 @@ TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) {
|
||||
EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
|
||||
}
|
||||
|
||||
// Test that id overflow is handled gracefully.
|
||||
TEST_F(ScalarReplacementTest, IdBoundOverflow) {
|
||||
const std::string text = R"(
|
||||
OpCapability ImageQuery
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpDecorate %4194302 DescriptorSet 1073495039
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypeStruct %6 %6
|
||||
%557056 = OpTypeStruct %7
|
||||
%9 = OpTypePointer Function %7
|
||||
%18 = OpTypeFunction %7 %9
|
||||
%4 = OpFunction %2 Pure|Const %3
|
||||
%1836763 = OpLabel
|
||||
%4194302 = OpVariable %9 Function
|
||||
%10 = OpVariable %9 Function
|
||||
OpKill
|
||||
%4194301 = OpLabel
|
||||
%524296 = OpLoad %7 %4194302
|
||||
OpKill
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
|
||||
std::vector<Message> messages = {
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."},
|
||||
{SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}};
|
||||
SetMessageConsumer(GetTestMessageConsumer(messages));
|
||||
auto result =
|
||||
SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, false);
|
||||
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
155
3rdparty/spirv-tools/test/opt/split_invalid_unreachable_test.cpp
vendored
Normal file
155
3rdparty/spirv-tools/test/opt/split_invalid_unreachable_test.cpp
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using SplitInvalidUnreachableTest = PassTest<::testing::Test>;
|
||||
|
||||
std::string spirv_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %1 "shader"
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%7 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::string function_head = R"(%1 = OpFunction %void None %7
|
||||
%8 = OpLabel
|
||||
OpBranch %9
|
||||
)";
|
||||
|
||||
std::string function_tail = "OpFunctionEnd\n";
|
||||
|
||||
std::string GetLoopMergeBlock(std::string block_id, std::string merge_id,
|
||||
std::string continue_id, std::string body_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpLoopMerge " + merge_id + " " + continue_id + " None\n";
|
||||
result += "OpBranch " + body_id + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetSelectionMergeBlock(std::string block_id,
|
||||
std::string condition_id,
|
||||
std::string merge_id, std::string true_id,
|
||||
std::string false_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += condition_id + " = OpSLessThan %bool %uint_1 %uint_2\n";
|
||||
result += "OpSelectionMerge " + merge_id + " None\n";
|
||||
result += "OpBranchConditional " + condition_id + " " + true_id + " " +
|
||||
false_id + "\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetReturnBlock(std::string block_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpReturn\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetUnreachableBlock(std::string block_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpUnreachable\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetBranchBlock(std::string block_id, std::string target_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpBranch " + target_id + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, NoInvalidBlocks) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
input += GetSelectionMergeBlock("%12", "%13", "%14", "%15", "%16");
|
||||
input += GetReturnBlock("%15");
|
||||
input += GetReturnBlock("%16");
|
||||
input += GetUnreachableBlock("%10");
|
||||
input += GetBranchBlock("%11", "%9");
|
||||
input += GetUnreachableBlock("%14");
|
||||
input += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, input,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, SelectionInLoop) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
input += GetSelectionMergeBlock("%12", "%13", "%11", "%15", "%16");
|
||||
input += GetReturnBlock("%15");
|
||||
input += GetReturnBlock("%16");
|
||||
input += GetUnreachableBlock("%10");
|
||||
input += GetBranchBlock("%11", "%9");
|
||||
input += function_tail;
|
||||
|
||||
std::string expected = spirv_header + function_head;
|
||||
expected += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
expected += GetSelectionMergeBlock("%12", "%13", "%16", "%14", "%15");
|
||||
expected += GetReturnBlock("%14");
|
||||
expected += GetReturnBlock("%15");
|
||||
expected += GetUnreachableBlock("%10");
|
||||
expected += GetUnreachableBlock("%16");
|
||||
expected += GetBranchBlock("%11", "%9");
|
||||
expected += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, LoopInSelection) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetSelectionMergeBlock("%9", "%10", "%11", "%12", "%13");
|
||||
input += GetLoopMergeBlock("%12", "%14", "%11", "%15");
|
||||
input += GetReturnBlock("%13");
|
||||
input += GetUnreachableBlock("%14");
|
||||
input += GetBranchBlock("%11", "%12");
|
||||
input += GetReturnBlock("%15");
|
||||
input += function_tail;
|
||||
|
||||
std::string expected = spirv_header + function_head;
|
||||
expected += GetSelectionMergeBlock("%9", "%10", "%16", "%12", "%13");
|
||||
expected += GetLoopMergeBlock("%12", "%14", "%11", "%15");
|
||||
expected += GetReturnBlock("%13");
|
||||
expected += GetUnreachableBlock("%14");
|
||||
expected += GetUnreachableBlock("%16");
|
||||
expected += GetBranchBlock("%11", "%12");
|
||||
expected += GetReturnBlock("%15");
|
||||
expected += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
@@ -1713,4 +1713,328 @@ OpFunctionEnd
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryAddOperands) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None None
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperand) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Nontemporal Nontemporal
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Nontemporal
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperands) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned 4
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherent) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %dst Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentPreviousArgs) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 4
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %dst Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherent) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %src Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherentPreviousArgs) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[scope]]
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %src Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherent) {
|
||||
const std::string text = R"(
|
||||
; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[wg]] MakePointerVisibleKHR|NonPrivatePointerKHR [[queue]]
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %src Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%ptr_wg_int = OpTypePointer Workgroup %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_wg_int Workgroup
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherentPreviousArgs) {
|
||||
const std::string text = R"(
|
||||
; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[queue]] Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[wg]]
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %dst Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%ptr_wg_int = OpTypePointer Workgroup %int
|
||||
%src = OpVariable %ptr_wg_int Workgroup
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatile) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile Volatile
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %src Volatile
|
||||
OpDecorate %dst Volatile
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatilePreviousArgs) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|Aligned 4 Volatile|Aligned 4
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %src Volatile
|
||||
OpDecorate %dst Volatile
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentTwoOperands) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %dst Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src None None
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(UpgradeMemoryModelTest,
|
||||
SPV14CopyMemoryDstCoherentPreviousArgsTwoOperands) {
|
||||
const std::string text = R"(
|
||||
; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
|
||||
; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 8
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %func "func" %src %dst
|
||||
OpDecorate %dst Coherent
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
|
||||
%src = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%dst = OpVariable %ptr_ssbo_int StorageBuffer
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %dst %src Aligned 4 Aligned 8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
|
||||
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -60,6 +60,29 @@ TEST_P(OpDecorateSimpleTest, AnySimpleDecoration) {
|
||||
Eq(input.str()));
|
||||
}
|
||||
|
||||
// Like above, but parameters to the decoration are IDs.
|
||||
using OpDecorateSimpleIdTest =
|
||||
spvtest::TextToBinaryTestBase<::testing::TestWithParam<
|
||||
std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>;
|
||||
|
||||
TEST_P(OpDecorateSimpleIdTest, AnySimpleDecoration) {
|
||||
// This string should assemble, but should not validate.
|
||||
std::stringstream input;
|
||||
input << "OpDecorateId %1 " << std::get<1>(GetParam()).name();
|
||||
for (auto operand : std::get<1>(GetParam()).operands())
|
||||
input << " %" << operand;
|
||||
input << std::endl;
|
||||
EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())),
|
||||
Eq(MakeInstruction(SpvOpDecorateId,
|
||||
{1, uint32_t(std::get<1>(GetParam()).value())},
|
||||
std::get<1>(GetParam()).operands())));
|
||||
// Also check disassembly.
|
||||
EXPECT_THAT(
|
||||
EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE,
|
||||
std::get<0>(GetParam())),
|
||||
Eq(input.str()));
|
||||
}
|
||||
|
||||
#define CASE(NAME) SpvDecoration##NAME, #NAME
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TextToBinaryDecorateSimple, OpDecorateSimpleTest,
|
||||
@@ -112,6 +135,21 @@ INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV11, OpDecorateSimpleTest,
|
||||
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
|
||||
Values(EnumCase<SpvDecoration>{
|
||||
CASE(MaxByteOffset), {128}})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest,
|
||||
Combine(Values(SPV_ENV_UNIVERSAL_1_4),
|
||||
ValuesIn(std::vector<EnumCase<SpvDecoration>>{
|
||||
{CASE(Uniform), {}},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleIdV14,
|
||||
OpDecorateSimpleIdTest,
|
||||
Combine(Values(SPV_ENV_UNIVERSAL_1_4),
|
||||
ValuesIn(std::vector<EnumCase<SpvDecoration>>{
|
||||
// In 1.4, UniformId decoration takes a
|
||||
// scope Id.
|
||||
{CASE(UniformId), {1}},
|
||||
})));
|
||||
#undef CASE
|
||||
|
||||
TEST_F(OpDecorateSimpleTest, WrongDecoration) {
|
||||
|
||||
49
3rdparty/spirv-tools/test/text_to_binary.composite_test.cpp
vendored
Normal file
49
3rdparty/spirv-tools/test/text_to_binary.composite_test.cpp
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2018 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.
|
||||
|
||||
// Assembler tests for instructions in the "Group Instrucions" section of the
|
||||
// SPIR-V spec.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
namespace spvtools {
|
||||
namespace {
|
||||
|
||||
using spvtest::Concatenate;
|
||||
|
||||
using CompositeRoundTripTest = RoundTripTest;
|
||||
|
||||
TEST_F(CompositeRoundTripTest, Good) {
|
||||
std::string spirv = "%2 = OpCopyLogical %1 %3\n";
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(CompositeRoundTripTest, V13Bad) {
|
||||
std::string spirv = "%2 = OpCopyLogical %1 %3\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpCopyLogical'"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spvtools
|
||||
@@ -276,11 +276,11 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
{"OpCapability StorageBuffer16BitAccess\n",
|
||||
MakeInstruction(SpvOpCapability,
|
||||
{SpvCapabilityStorageBuffer16BitAccess})},
|
||||
{"OpCapability StorageUniform16\n",
|
||||
{"OpCapability UniformAndStorageBuffer16BitAccess\n",
|
||||
MakeInstruction(
|
||||
SpvOpCapability,
|
||||
{SpvCapabilityUniformAndStorageBuffer16BitAccess})},
|
||||
{"OpCapability StorageUniform16\n",
|
||||
{"OpCapability UniformAndStorageBuffer16BitAccess\n",
|
||||
MakeInstruction(SpvOpCapability,
|
||||
{SpvCapabilityStorageUniform16})},
|
||||
{"OpCapability StoragePushConstant16\n",
|
||||
@@ -656,8 +656,38 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
// SPV_GOOGLE_decorate_string
|
||||
|
||||
// Now that OpDecorateString is the preferred spelling for
|
||||
// OpDecorateStringGOOGLE use that name in round trip tests, and the GOOGLE
|
||||
// name in an assembly-only test.
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_decorate_string, ExtensionRoundTripTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
|
||||
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
|
||||
ValuesIn(std::vector<AssemblyCase>{
|
||||
{"OpDecorateString %1 UserSemantic \"ABC\"\n",
|
||||
MakeInstruction(SpvOpDecorateStringGOOGLE,
|
||||
{1, SpvDecorationHlslSemanticGOOGLE},
|
||||
MakeVector("ABC"))},
|
||||
{"OpDecorateString %1 UserSemantic \"ABC\"\n",
|
||||
MakeInstruction(SpvOpDecorateString,
|
||||
{1, SpvDecorationUserSemantic},
|
||||
MakeVector("ABC"))},
|
||||
{"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
|
||||
MakeInstruction(SpvOpMemberDecorateStringGOOGLE,
|
||||
{1, 3, SpvDecorationUserSemantic},
|
||||
MakeVector("DEF"))},
|
||||
{"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n",
|
||||
MakeInstruction(SpvOpMemberDecorateString,
|
||||
{1, 3, SpvDecorationUserSemantic},
|
||||
MakeVector("DEF"))},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_decorate_string, ExtensionAssemblyTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
@@ -676,8 +706,29 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
|
||||
// SPV_GOOGLE_hlsl_functionality1
|
||||
|
||||
// Now that CounterBuffer is the preferred spelling for HlslCounterBufferGOOGLE,
|
||||
// use that name in round trip tests, and the GOOGLE name in an assembly-only
|
||||
// test.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_hlsl_functionality1, ExtensionRoundTripTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
|
||||
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0),
|
||||
// HlslSemanticGOOGLE is tested in SPV_GOOGLE_decorate_string, since
|
||||
// they are coupled together.
|
||||
ValuesIn(std::vector<AssemblyCase>{
|
||||
{"OpDecorateId %1 CounterBuffer %2\n",
|
||||
MakeInstruction(SpvOpDecorateId,
|
||||
{1, SpvDecorationHlslCounterBufferGOOGLE, 2})},
|
||||
{"OpDecorateId %1 CounterBuffer %2\n",
|
||||
MakeInstruction(SpvOpDecorateId,
|
||||
{1, SpvDecorationCounterBuffer, 2})},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_GOOGLE_hlsl_functionality1, ExtensionAssemblyTest,
|
||||
Combine(
|
||||
// We'll get coverage over operand tables by trying the universal
|
||||
// environments, and at least one specific environment.
|
||||
|
||||
@@ -30,6 +30,7 @@ using spvtest::EnumCase;
|
||||
using spvtest::MakeInstruction;
|
||||
using spvtest::TextToBinaryTest;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
// Test assembly of Memory Access masks
|
||||
|
||||
@@ -95,12 +96,326 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
#undef CASE
|
||||
// clang-format on
|
||||
|
||||
using MemoryRoundTripTest = RoundTripTest;
|
||||
|
||||
// OpPtrEqual appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrEqualGood) {
|
||||
std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrEqual, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) {
|
||||
std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'"));
|
||||
}
|
||||
|
||||
// OpPtrNotEqual appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) {
|
||||
std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrNotEqual, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) {
|
||||
std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'"));
|
||||
}
|
||||
|
||||
// OpPtrDiff appeared in SPIR-V 1.4
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrDiffGood) {
|
||||
std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpPtrDiff, {1, 2, 3, 4})));
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) {
|
||||
// OpPtrDiff is enabled by a capability as well, so we can assemble
|
||||
// it even in older SPIR-V environments. We do that so we can
|
||||
// write tests.
|
||||
std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
|
||||
std::string disassembly = EncodeAndDecodeSuccessfully(
|
||||
spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
|
||||
}
|
||||
|
||||
// OpCopyMemory
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) {
|
||||
std::string spirv = "OpCopyMemory %1\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 %3\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 None\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 0})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 2, 8})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailableKHR %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 8, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 MakePointerVisibleKHR %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 16, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointerKHR\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 32})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) {
|
||||
std::string spirv =
|
||||
"OpCopyMemory %1 %2 "
|
||||
"Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
|
||||
"MakePointerVisibleKHR|NonPrivatePointerKHR 16 %3 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 63, 16, 3, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
|
||||
// Note: This will assemble but should not validate for SPIR-V 1.3
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) {
|
||||
std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) {
|
||||
std::string spirv =
|
||||
"OpCopyMemory %1 %2 Volatile|Nontemporal|"
|
||||
"MakePointerVisibleKHR %3 "
|
||||
"Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
// OpCopyMemorySized
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n";
|
||||
std::string err = CompileFailure(spirv);
|
||||
EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'"));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 0})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 2, 8})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailableKHR %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 8, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisibleKHR %4\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 16, 4})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointerKHR\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 32})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) {
|
||||
std::string spirv =
|
||||
"OpCopyMemorySized %1 %2 %3 "
|
||||
"Volatile|Aligned|Nontemporal|MakePointerAvailableKHR|"
|
||||
"MakePointerVisibleKHR|NonPrivatePointerKHR 16 %4 %5\n";
|
||||
EXPECT_THAT(
|
||||
CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
|
||||
// Note: This will assemble but should not validate for SPIR-V 1.3
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) {
|
||||
std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
|
||||
EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) {
|
||||
std::string spirv =
|
||||
"OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|"
|
||||
"MakePointerVisibleKHR %4 "
|
||||
"Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 16 %5\n";
|
||||
EXPECT_THAT(
|
||||
CompiledInstructions(spirv),
|
||||
Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5})));
|
||||
std::string disassembly =
|
||||
EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
||||
EXPECT_THAT(disassembly, Eq(spirv));
|
||||
}
|
||||
|
||||
// TODO(dneto): OpVariable with initializers
|
||||
// TODO(dneto): OpImageTexelPointer
|
||||
// TODO(dneto): OpLoad
|
||||
// TODO(dneto): OpStore
|
||||
// TODO(dneto): OpCopyMemory
|
||||
// TODO(dneto): OpCopyMemorySized
|
||||
// TODO(dneto): OpAccessChain
|
||||
// TODO(dneto): OpInBoundsAccessChain
|
||||
// TODO(dneto): OpPtrAccessChain
|
||||
|
||||
23
3rdparty/spirv-tools/test/tools/expect.py
vendored
23
3rdparty/spirv-tools/test/tools/expect.py
vendored
@@ -26,6 +26,8 @@ import traceback
|
||||
from spirv_test_framework import SpirvTest
|
||||
from builtins import bytes
|
||||
|
||||
DEFAULT_SPIRV_VERSION = 0x010000
|
||||
|
||||
def convert_to_unix_line_endings(source):
|
||||
"""Converts all line endings in source to be unix line endings."""
|
||||
result = source.replace('\r\n', '\n').replace('\r', '\n')
|
||||
@@ -172,7 +174,7 @@ class CorrectBinaryLengthAndPreamble(SpirvTest):
|
||||
# profile
|
||||
|
||||
if version != spv_version and version != 0:
|
||||
return False, 'Incorrect SPV binary: wrong version number'
|
||||
return False, 'Incorrect SPV binary: wrong version number: ' + hex(version) + ' expected ' + hex(spv_version)
|
||||
# Shaderc-over-Glslang (0x000d....) or
|
||||
# SPIRV-Tools (0x0007....) generator number
|
||||
if read_word(preamble, 2, little_endian) != 0x000d0007 and \
|
||||
@@ -188,7 +190,9 @@ class CorrectBinaryLengthAndPreamble(SpirvTest):
|
||||
class CorrectObjectFilePreamble(CorrectBinaryLengthAndPreamble):
|
||||
"""Provides methods for verifying preamble for a SPV object file."""
|
||||
|
||||
def verify_object_file_preamble(self, filename, spv_version=0x10000):
|
||||
def verify_object_file_preamble(self,
|
||||
filename,
|
||||
spv_version=DEFAULT_SPIRV_VERSION):
|
||||
"""Checks that the given SPIR-V binary file has correct preamble."""
|
||||
|
||||
success, message = verify_file_non_empty(filename)
|
||||
@@ -257,6 +261,21 @@ class ValidObjectFile1_3(ReturnCodeIsZero, CorrectObjectFilePreamble):
|
||||
return True, ''
|
||||
|
||||
|
||||
class ValidObjectFile1_4(ReturnCodeIsZero, CorrectObjectFilePreamble):
|
||||
"""Mixin class for checking that every input file generates a valid SPIR-V 1.4
|
||||
object file following the object file naming rule, and there is no output on
|
||||
stdout/stderr."""
|
||||
|
||||
def check_object_file_preamble(self, status):
|
||||
for input_filename in status.input_filenames:
|
||||
object_filename = get_object_filename(input_filename)
|
||||
success, message = self.verify_object_file_preamble(
|
||||
os.path.join(status.directory, object_filename), 0x10400)
|
||||
if not success:
|
||||
return False, message
|
||||
return True, ''
|
||||
|
||||
|
||||
class ValidObjectFileWithAssemblySubstr(SuccessfulReturn,
|
||||
CorrectObjectFilePreamble):
|
||||
"""Mixin class for checking that every input file generates a valid object
|
||||
|
||||
10
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
10
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
@@ -34,7 +34,7 @@ def empty_main_assembly():
|
||||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptBase')
|
||||
class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_3):
|
||||
class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_4):
|
||||
"""Tests that spirv-opt accepts a SPIR-V object file."""
|
||||
|
||||
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
|
||||
@@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch):
|
||||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestValidPassFlags(expect.ValidObjectFile1_3,
|
||||
class TestValidPassFlags(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt accepts all valid optimization flags."""
|
||||
|
||||
@@ -129,7 +129,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_3,
|
||||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by -O."""
|
||||
|
||||
@@ -176,7 +176,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestSizeOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestSizeOptimizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by -Os."""
|
||||
|
||||
@@ -215,7 +215,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_3,
|
||||
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestLegalizationPasses(expect.ValidObjectFile1_3,
|
||||
class TestLegalizationPasses(expect.ValidObjectFile1_4,
|
||||
expect.ExecutedListOfPasses):
|
||||
"""Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl.
|
||||
"""
|
||||
|
||||
1
3rdparty/spirv-tools/test/val/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/test/val/CMakeLists.txt
vendored
@@ -63,6 +63,7 @@ add_spvtools_unittest(TARGET val_fghijklmnop
|
||||
val_memory_test.cpp
|
||||
val_modes_test.cpp
|
||||
val_non_uniform_test.cpp
|
||||
val_opencl_test.cpp
|
||||
val_primitives_test.cpp
|
||||
${VAL_TEST_COMMON_SRCS}
|
||||
LIBS ${SPIRV_TOOLS}
|
||||
|
||||
@@ -1975,6 +1975,29 @@ TEST_F(ValidateAtomics, WebGPUQueueFamilyMemoryScopeGood) {
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, CompareExchangeWeakV13ValV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, CompareExchangeWeakV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -954,8 +954,8 @@ TEST_F(ValidateBarriers, TypeAsMemoryScope) {
|
||||
OpMemoryBarrier %u32 %u32_0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%uint] cannot be a "
|
||||
"type"));
|
||||
}
|
||||
|
||||
@@ -2265,8 +2265,8 @@ OpMemoryModel Logical Simple
|
||||
EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("operand 5255 requires one of these extensions: "
|
||||
"SPV_NV_viewport_array2"));
|
||||
HasSubstr("operand ShaderViewportMaskNV(5255) requires one of "
|
||||
"these extensions: SPV_NV_viewport_array2"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCapability,
|
||||
@@ -2347,8 +2347,8 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("operand 5568 requires one of these extensions: "
|
||||
"SPV_INTEL_subgroups"));
|
||||
HasSubstr("operand SubgroupShuffleINTEL(5568) requires one of "
|
||||
"these extensions: SPV_INTEL_subgroups"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCapability,
|
||||
@@ -2407,6 +2407,33 @@ OpMemoryModel Logical GLSL450
|
||||
"specified if the VulkanKHR memory model is used"));
|
||||
}
|
||||
|
||||
// In the grammar, SubgroupEqMask and SubgroupMaskKHR have different enabling
|
||||
// lists of extensions.
|
||||
TEST_F(ValidateCapability, SubgroupEqMaskEnabledByExtension) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability SubgroupBallotKHR
|
||||
OpExtension "SPV_KHR_shader_ballot"
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpDecorate %var BuiltIn SubgroupEqMask
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%ptr_uint = OpTypePointer Private %uint
|
||||
%var = OpVariable %ptr_uint Private
|
||||
%fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
%val = OpLoad %uint %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0))
|
||||
<< getDiagnosticString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
377
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
377
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
@@ -1952,7 +1952,7 @@ TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
|
||||
%funct = OpTypeFunction %voidt
|
||||
%main = OpFunction %voidt None %funct
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %exit None
|
||||
OpLoopMerge %exit %loop None
|
||||
OpBranch %exit
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
@@ -3028,6 +3028,381 @@ OpFunctionEnd
|
||||
"to the loop header <ID> 7"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %undef %2 None
|
||||
OpBranchConditional %undef %2 %2
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Merge Block 1[%undef] must be an OpLabel"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %undef None
|
||||
OpBranchConditional %undef %2 %2
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Continue Target 1[%undef] must be an OpLabel"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %2 None
|
||||
OpBranchConditional %undef %2 %2
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Merge Block and Continue Target must be different ids"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeUnrollAndDontUnroll) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%5 = OpLabel
|
||||
OpBranch %1
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %3 Unroll|DontUnroll
|
||||
OpBranchConditional %undef %2 %3
|
||||
%3 = OpLabel
|
||||
OpBranch %1
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Unroll and DontUnroll loop controls must not both be specified"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergePeelCountAndDontUnroll) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%5 = OpLabel
|
||||
OpBranch %1
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %3 DontUnroll|PeelCount 1
|
||||
OpBranchConditional %undef %2 %3
|
||||
%3 = OpLabel
|
||||
OpBranch %1
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"PeelCount and DontUnroll loop controls must not both be specified"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergePartialCountAndDontUnroll) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%5 = OpLabel
|
||||
OpBranch %1
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %3 DontUnroll|PartialCount 1
|
||||
OpBranchConditional %undef %2 %3
|
||||
%3 = OpLabel
|
||||
OpBranch %1
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("PartialCount and DontUnroll loop controls must not "
|
||||
"both be specified"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeIterationMultipleZero) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%5 = OpLabel
|
||||
OpBranch %1
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %3 IterationMultiple 0
|
||||
OpBranchConditional %undef %2 %3
|
||||
%3 = OpLabel
|
||||
OpBranch %1
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"IterationMultiple loop control operand must be greater than zero"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopMergeIterationMultipleZeroMoreOperands) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %undef "undef"
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%5 = OpLabel
|
||||
OpBranch %1
|
||||
%1 = OpLabel
|
||||
OpLoopMerge %2 %3 MaxIterations|IterationMultiple 4 0
|
||||
OpBranchConditional %undef %2 %3
|
||||
%3 = OpLabel
|
||||
OpBranch %1
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"IterationMultiple loop control operand must be greater than zero"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, InvalidSelectionExit) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %1 "main"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeBool
|
||||
%4 = OpConstantTrue %3
|
||||
%5 = OpTypeFunction %2
|
||||
%1 = OpFunction %2 None %5
|
||||
%6 = OpLabel
|
||||
OpSelectionMerge %7 None
|
||||
OpBranchConditional %4 %7 %8
|
||||
%8 = OpLabel
|
||||
OpSelectionMerge %9 None
|
||||
OpBranchConditional %4 %10 %9
|
||||
%10 = OpLabel
|
||||
OpBranch %7
|
||||
%9 = OpLabel
|
||||
OpBranch %7
|
||||
%7 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("block <ID> 10[%10] exits the selection headed by <ID> "
|
||||
"8[%8], but not via a structured exit"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, InvalidLoopExit) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %1 "main"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeBool
|
||||
%4 = OpConstantTrue %3
|
||||
%5 = OpTypeFunction %2
|
||||
%1 = OpFunction %2 None %5
|
||||
%6 = OpLabel
|
||||
OpSelectionMerge %7 None
|
||||
OpBranchConditional %4 %7 %8
|
||||
%8 = OpLabel
|
||||
OpLoopMerge %9 %10 None
|
||||
OpBranchConditional %4 %9 %11
|
||||
%11 = OpLabel
|
||||
OpBranchConditional %4 %7 %10
|
||||
%10 = OpLabel
|
||||
OpBranch %8
|
||||
%9 = OpLabel
|
||||
OpBranch %7
|
||||
%7 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("block <ID> 11[%11] exits the loop headed by <ID> "
|
||||
"8[%8], but not via a structured exit"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, InvalidContinueExit) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %1 "main"
|
||||
OpExecutionMode %1 OriginUpperLeft
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeBool
|
||||
%4 = OpConstantTrue %3
|
||||
%5 = OpTypeFunction %2
|
||||
%1 = OpFunction %2 None %5
|
||||
%6 = OpLabel
|
||||
OpSelectionMerge %7 None
|
||||
OpBranchConditional %4 %7 %8
|
||||
%8 = OpLabel
|
||||
OpLoopMerge %9 %10 None
|
||||
OpBranchConditional %4 %9 %10
|
||||
%10 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpBranchConditional %4 %8 %7
|
||||
%9 = OpLabel
|
||||
OpBranch %7
|
||||
%7 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("block <ID> 11[%11] exits the continue headed by <ID> "
|
||||
"10[%10], but not via a structured exit"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, InvalidSelectionExitBackedge) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%1 = OpTypeVoid
|
||||
%2 = OpTypeBool
|
||||
%3 = OpUndef %2
|
||||
%4 = OpTypeFunction %1
|
||||
%5 = OpFunction %1 None %4
|
||||
%6 = OpLabel
|
||||
OpBranch %7
|
||||
%7 = OpLabel
|
||||
OpLoopMerge %8 %9 None
|
||||
OpBranchConditional %3 %8 %9
|
||||
%9 = OpLabel
|
||||
OpSelectionMerge %10 None
|
||||
OpBranchConditional %3 %11 %12
|
||||
%11 = OpLabel
|
||||
OpBranch %13
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranch %7
|
||||
%10 = OpLabel
|
||||
OpUnreachable
|
||||
%8 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("block <ID> 13[%13] exits the selection headed by <ID> "
|
||||
"9[%9], but not via a structured exit"));
|
||||
}
|
||||
|
||||
/// TODO(umar): Nested CFG constructs
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1568,6 +1568,209 @@ OpFunctionEnd
|
||||
HasSubstr("Expected Index to be int scalar"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalSameType) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%struct = OpTypeStruct
|
||||
%const_struct = OpConstantComposite %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must not equal the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalSameStructDifferentId) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%struct1 = OpTypeStruct
|
||||
%struct2 = OpTypeStruct
|
||||
%const_struct = OpConstantComposite %struct1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayDifferentLength) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_4 = OpConstant %int 4
|
||||
%int_5 = OpConstant %int 5
|
||||
%array1 = OpTypeArray %int %int_4
|
||||
%array2 = OpTypeArray %int %int_5
|
||||
%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayDifferentElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 0
|
||||
%int_4 = OpConstant %int 4
|
||||
%array1 = OpTypeArray %int %int_4
|
||||
%array2 = OpTypeArray %float %int_4
|
||||
%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalArrayLogicallyMatchedElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%inner1 = OpTypeArray %int %int_1
|
||||
%inner2 = OpTypeArray %int %int_1
|
||||
%array1 = OpTypeArray %inner1 %int_1
|
||||
%array2 = OpTypeArray %inner2 %int_1
|
||||
%const_inner = OpConstantComposite %inner1 %int_1
|
||||
%const_array = OpConstantComposite %array1 %const_inner
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %array2 %const_array
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructDifferentNumberElements) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct1 = OpTypeStruct
|
||||
%struct2 = OpTypeStruct %int
|
||||
%const_struct = OpConstantComposite %struct1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructDifferentElement) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%struct1 = OpTypeStruct %int %uint
|
||||
%struct2 = OpTypeStruct %int %int
|
||||
%const_struct = OpConstantComposite %struct1 %int_0 %uint_0
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Result Type does not logically match the Operand type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateComposites, CopyLogicalStructLogicallyMatch) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%array1 = OpTypeArray %int %int_1
|
||||
%array2 = OpTypeArray %int %int_1
|
||||
%struct1 = OpTypeStruct %int %array1
|
||||
%struct2 = OpTypeStruct %int %array2
|
||||
%const_array = OpConstantComposite %array1 %int_1
|
||||
%const_struct = OpConstantComposite %struct1 %int_1 %const_array
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%copy = OpCopyLogical %struct2 %const_struct
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -198,6 +198,60 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
"%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %uint_0"),
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UConvert, ValidateConstantOp,
|
||||
ValuesIn(std::vector<ConstantOpCase>{
|
||||
// TODO(dneto): Conversions must change width.
|
||||
{SPV_ENV_UNIVERSAL_1_0,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_1,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_4,
|
||||
kKernelPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
{SPV_ENV_UNIVERSAL_1_0,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_1,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_3,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
false,
|
||||
"Prior to SPIR-V 1.4, specialization constant operation "
|
||||
"UConvert requires Kernel capability"},
|
||||
{SPV_ENV_UNIVERSAL_1_4,
|
||||
kShaderPreamble kBasicTypes
|
||||
"%v = OpSpecConstantOp %uint UConvert %uint_0",
|
||||
true, ""},
|
||||
}));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
KernelInKernel, ValidateConstantOp,
|
||||
ValuesIn(std::vector<ConstantOpCase>{
|
||||
@@ -294,6 +348,25 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
true, ""},
|
||||
}));
|
||||
|
||||
TEST_F(ValidateConstant, SpecConstantUConvert1p3Binary1p4EnvBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%int = OpTypeInt 32 0
|
||||
%int0 = OpConstant %int 0
|
||||
%const = OpSpecConstantOp %int UConvert %int0
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Prior to SPIR-V 1.4, specialization constant operation UConvert "
|
||||
"requires Kernel capability or extension SPV_AMD_gpu_shader_int16"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -778,9 +778,10 @@ TEST_F(ValidateData, vulkan_disallow_free_fp_rounding_mode) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Operand 2 of Decorate requires one of these capabilities: "
|
||||
"StorageBuffer16BitAccess StorageUniform16 "
|
||||
"StoragePushConstant16 StorageInputOutput16"));
|
||||
HasSubstr(
|
||||
"Operand 2 of Decorate requires one of these capabilities: "
|
||||
"StorageBuffer16BitAccess UniformAndStorageBuffer16BitAccess "
|
||||
"StoragePushConstant16 StorageInputOutput16"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "source/val/decoration.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
#include "test/val/val_code_generator.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
@@ -2099,7 +2100,7 @@ TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BlockArrayBaseAlignmentGood) {
|
||||
TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) {
|
||||
// For uniform buffer, Array base alignment is 16, and ArrayStride
|
||||
// must be a multiple of 16.
|
||||
std::string spirv = R"(
|
||||
@@ -2132,7 +2133,7 @@ TEST_F(ValidateDecorations, BlockArrayBaseAlignmentGood) {
|
||||
<< getDiagnosticString();
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BlockArrayBadAlignmentBad) {
|
||||
TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) {
|
||||
// For uniform buffer, Array base alignment is 16.
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@@ -2169,7 +2170,7 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentBad) {
|
||||
"member 1 at offset 8 is not aligned to 16"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithRelaxedLayoutStillBad) {
|
||||
TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) {
|
||||
// For uniform buffer, Array base alignment is 16, and ArrayStride
|
||||
// must be a multiple of 16. This case uses relaxed block layout. Relaxed
|
||||
// layout only relaxes rules for vector alignment, not array alignment.
|
||||
@@ -2212,7 +2213,7 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithRelaxedLayoutStillBad) {
|
||||
"member 1 at offset 8 is not aligned to 16"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
|
||||
TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) {
|
||||
// Same as previous test, but with Vulkan 1.1, which includes
|
||||
// VK_KHR_relaxed_block_layout in core.
|
||||
std::string spirv = R"(
|
||||
@@ -2253,6 +2254,44 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
|
||||
"member 1 at offset 8 is not aligned to 16"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations,
|
||||
BlockArrayBaseAlignmentWithBlockStandardLayoutGood) {
|
||||
// Same as previous test, but with VK_KHR_uniform_buffer_standard_layout
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %main "main"
|
||||
OpSource GLSL 450
|
||||
OpDecorate %_arr_float_uint_2 ArrayStride 16
|
||||
OpDecorate %u DescriptorSet 0
|
||||
OpDecorate %u Binding 0
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpMemberDecorate %S 1 Offset 8
|
||||
OpDecorate %S Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%_arr_float_uint_2 = OpTypeArray %float %uint_2
|
||||
%S = OpTypeStruct %v2float %_arr_float_uint_2
|
||||
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||
%u = OpVariable %_ptr_Uniform_S Uniform
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(),
|
||||
true);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@@ -4708,7 +4747,7 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
// Uniform decoration
|
||||
// Uniform and UniformId decorations
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationGood) {
|
||||
const std::string spirv = R"(
|
||||
@@ -4737,49 +4776,96 @@ OpFunctionEnd
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %fn Uniform
|
||||
%void = OpTypeVoid
|
||||
%fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Uniform decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%2 = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv = R"(
|
||||
// Returns SPIR-V assembly for a shader that uses a given decoration
|
||||
// instruction.
|
||||
std::string ShaderWithUniformLikeDecoration(const std::string& inst) {
|
||||
return std::string(R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %subgroupscope "subgroupscope"
|
||||
OpName %call "call"
|
||||
OpName %myfunc "myfunc"
|
||||
OpDecorate %call Uniform
|
||||
OpName %int0 "int0"
|
||||
OpName %float0 "float0"
|
||||
OpName %fn "fn"
|
||||
)") + inst +
|
||||
R"(
|
||||
%void = OpTypeVoid
|
||||
%fnty = OpTypeFunction %void
|
||||
%myfunc = OpFunction %void None %fnty
|
||||
%float = OpTypeFloat 32
|
||||
%int = OpTypeInt 32 1
|
||||
%int0 = OpConstantNull %int
|
||||
%int_99 = OpConstant %int 99
|
||||
%subgroupscope = OpConstant %int 3
|
||||
%float0 = OpConstantNull %float
|
||||
%fn = OpTypeFunction %void
|
||||
%myfunc = OpFunction %void None %fn
|
||||
%myfuncentry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %fnty
|
||||
%main = OpFunction %void None %fn
|
||||
%entry = OpLabel
|
||||
%call = OpFunctionCall %void %myfunc
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13Bad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later\n"
|
||||
" OpDecorateId %int0 UniformId %subgroupscope"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13BadTargetV14) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV14Good) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %int0 UniformId %subgroupscope");
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorate %fn Uniform");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Uniform decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationTargetsTypeBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %fn UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("UniformId decoration applied to a non-object"));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorate %call Uniform");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
@@ -4788,6 +4874,82 @@ OpFunctionEnd
|
||||
" %call = OpFunctionCall %void %myfunc"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationTargetsVoidValueBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorateId %call UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4))
|
||||
<< spirv;
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("UniformId decoration applied to a value with void type\n"
|
||||
" %call = OpFunctionCall %void %myfunc"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations,
|
||||
UniformDecorationWithScopeIdV14IdIsFloatValueIsBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %float0");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("ConstantNull: expected Execution Scope to be a 32-bit int"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations,
|
||||
UniformDecorationWithScopeIdV14IdIsInvalidIntValueBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int_99");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Invalid scope value:\n %int_99 = OpConstant %int 99\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(": in Vulkan environment Execution Scope is limited to "
|
||||
"Workgroup and Subgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformDecorationWithWrongInstructionBad) {
|
||||
const std::string spirv =
|
||||
ShaderWithUniformLikeDecoration("OpDecorateId %int0 Uniform");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Decorations that don't take ID parameters may not be "
|
||||
"used with OpDecorateId\n"
|
||||
" OpDecorateId %int0 Uniform"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UniformIdDecorationWithWrongInstructionBad) {
|
||||
const std::string spirv = ShaderWithUniformLikeDecoration(
|
||||
"OpDecorate %int0 UniformId %subgroupscope");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Decorations taking ID parameters may not be used with OpDecorateId\n"
|
||||
" OpDecorate %int0 UniformId %subgroupscope"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, MultipleOffsetDecorationsOnSameID) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@@ -5070,6 +5232,26 @@ TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionBad) {
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionV13Bad) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires one of these extensions: "
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapOkInSPV14Good) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoSignedWrapIAddGood) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero");
|
||||
@@ -5192,6 +5374,26 @@ TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionBad) {
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionV13Bad) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires one of these extensions: "
|
||||
"SPV_KHR_no_integer_wrap_decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapOkInSPV14Good) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero", "");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NoUnsignedWrapIAddGood) {
|
||||
std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap",
|
||||
"%val = OpIAdd %int %zero %zero");
|
||||
@@ -5783,10 +5985,8 @@ std::string ShaderWithNonWritableTarget(const std::string& target,
|
||||
OpName %simple_struct "simple_struct"
|
||||
|
||||
OpDecorate %struct_b Block
|
||||
OpDecorate %struct_bb BufferBlock
|
||||
OpDecorate %struct_b_rtarr Block
|
||||
OpMemberDecorate %struct_b 0 Offset 0
|
||||
OpMemberDecorate %struct_bb 0 Offset 0
|
||||
OpMemberDecorate %struct_b_rtarr 0 Offset 0
|
||||
OpDecorate %rtarr ArrayStride 4
|
||||
)") + decoration_inst +
|
||||
@@ -5800,7 +6000,6 @@ std::string ShaderWithNonWritableTarget(const std::string& target,
|
||||
%int = OpTypeInt 32 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%struct_b = OpTypeStruct %float
|
||||
%struct_bb = OpTypeStruct %float
|
||||
%rtarr = OpTypeRuntimeArray %float
|
||||
%struct_b_rtarr = OpTypeStruct %rtarr
|
||||
%simple_struct = OpTypeStruct %float
|
||||
@@ -5812,7 +6011,6 @@ std::string ShaderWithNonWritableTarget(const std::string& target,
|
||||
%rta_imstor = OpTypeRuntimeArray %imstor
|
||||
|
||||
%_ptr_Uniform_stb = OpTypePointer Uniform %struct_b
|
||||
%_ptr_Uniform_stbb = OpTypePointer Uniform %struct_bb
|
||||
%_ptr_StorageBuffer_stb = OpTypePointer StorageBuffer %struct_b
|
||||
%_ptr_StorageBuffer_stb_rtarr = OpTypePointer StorageBuffer %struct_b_rtarr
|
||||
%_ptr_Workgroup = OpTypePointer Workgroup %float
|
||||
@@ -5826,7 +6024,6 @@ std::string ShaderWithNonWritableTarget(const std::string& target,
|
||||
%extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor
|
||||
|
||||
%var_ubo = OpVariable %_ptr_Uniform_stb Uniform
|
||||
%var_ssbo_u = OpVariable %_ptr_Uniform_stbb Uniform
|
||||
%var_ssbo_sb = OpVariable %_ptr_StorageBuffer_stb StorageBuffer
|
||||
%var_ssbo_sb_rtarr = OpVariable %_ptr_StorageBuffer_stb_rtarr StorageBuffer
|
||||
%var_wg = OpVariable %_ptr_Workgroup Workgroup
|
||||
@@ -5946,7 +6143,24 @@ TEST_F(ValidateDecorations, NonWritableVarUboGood) {
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarSsboInUniformGood) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_ssbo_u");
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %main "main"
|
||||
OpDecorate %struct_bb BufferBlock
|
||||
OpMemberDecorate %struct_bb 0 Offset 0
|
||||
OpDecorate %var_ssbo_u NonWritable
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%struct_bb = OpTypeStruct %float
|
||||
%_ptr_Uniform_stbb = OpTypePointer Uniform %struct_bb
|
||||
%var_ssbo_u = OpVariable %_ptr_Uniform_stbb Uniform
|
||||
%main = OpFunction %void None %void_fn
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
@@ -5987,6 +6201,18 @@ TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) {
|
||||
"buffer\n %var_wg"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_wg");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, storage "
|
||||
"buffer, or variable in Private or Function storage "
|
||||
"class\n %var_wg"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateBad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
@@ -5998,6 +6224,36 @@ TEST_F(ValidateDecorations, NonWritableVarPrivateBad) {
|
||||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_priv");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_priv"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionBad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
@@ -6084,6 +6340,66 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_ERROR_INVALID_ID,
|
||||
"is not valid for the WebGPU execution environment."))));
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_func"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) {
|
||||
std::string spirv = ShaderWithNonWritableTarget("%var_func");
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Target of NonWritable decoration is invalid: must "
|
||||
"point to a storage image, uniform block, or storage "
|
||||
"buffer\n %var_func"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BufferBlockV13ValV14Good) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorate %1 BufferBlock
|
||||
%1 = OpTypeStruct
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, BufferBlockV14Bad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorate %1 BufferBlock
|
||||
%1 = OpTypeStruct
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("2nd operand of Decorate: operand BufferBlock(3) "
|
||||
"requires SPIR-V version 1.3 or earlier"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -288,9 +288,12 @@ TEST_P(ValidateExtIntoCore, DoNotAskForExtensionInLaterVersion) {
|
||||
|
||||
CompileSuccessfully(code.c_str(), GetParam().env);
|
||||
if (GetParam().success) {
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env))
|
||||
<< getDiagnosticString();
|
||||
} else {
|
||||
ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env));
|
||||
ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env))
|
||||
<< " in " << spvTargetEnvDescription(GetParam().env) << ":\n"
|
||||
<< code;
|
||||
const std::string message = getDiagnosticString();
|
||||
if (spvIsVulkanEnv(GetParam().env)) {
|
||||
EXPECT_THAT(message, HasSubstr(std::string(GetParam().cap) +
|
||||
|
||||
26
3rdparty/spirv-tools/test/val/val_fixtures.h
vendored
26
3rdparty/spirv-tools/test/val/val_fixtures.h
vendored
@@ -21,6 +21,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "source/val/validation_state.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
|
||||
@@ -37,6 +38,11 @@ class ValidateBase : public ::testing::Test,
|
||||
// Returns the a spv_const_binary struct
|
||||
spv_const_binary get_const_binary();
|
||||
|
||||
// Assembles the given SPIR-V text, checks that it fails to assemble,
|
||||
// and returns resulting diagnostic. No internal state is updated.
|
||||
std::string CompileFailure(std::string code,
|
||||
spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
|
||||
|
||||
// Checks that 'code' is valid SPIR-V text representation and stores the
|
||||
// binary version for further method calls.
|
||||
void CompileSuccessfully(std::string code,
|
||||
@@ -100,17 +106,31 @@ void ValidateBase<T>::TearDown() {
|
||||
spvValidatorOptionsDestroy(options_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string ValidateBase<T>::CompileFailure(std::string code,
|
||||
spv_target_env env) {
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
EXPECT_NE(SPV_SUCCESS,
|
||||
spvTextToBinary(ScopedContext(env).context, code.c_str(),
|
||||
code.size(), &binary_, &diagnostic));
|
||||
std::string result(diagnostic->error);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ValidateBase<T>::CompileSuccessfully(std::string code,
|
||||
spv_target_env env) {
|
||||
DestroyBinary();
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
ASSERT_EQ(SPV_SUCCESS,
|
||||
spvTextToBinary(ScopedContext(env).context, code.c_str(),
|
||||
code.size(), &binary_, &diagnostic))
|
||||
ScopedContext context(env);
|
||||
auto status = spvTextToBinary(context.context, code.c_str(), code.size(),
|
||||
&binary_, &diagnostic);
|
||||
EXPECT_EQ(SPV_SUCCESS, status)
|
||||
<< "ERROR: " << diagnostic->error
|
||||
<< "\nSPIR-V could not be compiled into binary:\n"
|
||||
<< code;
|
||||
ASSERT_EQ(SPV_SUCCESS, status);
|
||||
spvDiagnosticDestroy(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
417
3rdparty/spirv-tools/test/val/val_function_test.cpp
vendored
417
3rdparty/spirv-tools/test/val/val_function_test.cpp
vendored
@@ -416,6 +416,423 @@ TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationVariablePointers) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMatchingPointers) {
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||
OpMemberDecorate %_struct_5 0 Offset 0
|
||||
OpDecorate %_struct_5 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %int
|
||||
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_struct_15 = OpTypeStruct %int
|
||||
%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15
|
||||
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||
%18 = OpTypeFunction %void %_ptr_Function__struct_15
|
||||
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0
|
||||
%21 = OpFunctionCall %void %22 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%22 = OpFunction %void None %18
|
||||
%23 = OpFunctionParameter %_ptr_Function__struct_15
|
||||
%24 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMatchingPointersNestedStruct) {
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpMemberDecorate %_struct_4 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_4 ArrayStride 4
|
||||
OpMemberDecorate %_struct_6 0 Offset 0
|
||||
OpDecorate %_struct_6 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %int
|
||||
%_struct_4 = OpTypeStruct %_struct_3
|
||||
%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4
|
||||
%_struct_6 = OpTypeStruct %_runtimearr__struct_4
|
||||
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
|
||||
%void = OpTypeVoid
|
||||
%13 = OpTypeFunction %void
|
||||
%_struct_14 = OpTypeStruct %int
|
||||
%_struct_15 = OpTypeStruct %_struct_14
|
||||
%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15
|
||||
%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
|
||||
%18 = OpTypeFunction %void %_ptr_Function__struct_15
|
||||
%2 = OpVariable %_ptr_Uniform__struct_6 Uniform
|
||||
%1 = OpFunction %void None %13
|
||||
%19 = OpLabel
|
||||
%20 = OpVariable %_ptr_Function__struct_15 Function
|
||||
%21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0
|
||||
%22 = OpFunctionCall %void %23 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%23 = OpFunction %void None %18
|
||||
%24 = OpFunctionParameter %_ptr_Function__struct_15
|
||||
%25 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMatchingPointersNestedArray) {
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpDecorate %_arr_int_uint_10 ArrayStride 4
|
||||
OpMemberDecorate %_struct_4 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_4 ArrayStride 40
|
||||
OpMemberDecorate %_struct_6 0 Offset 0
|
||||
OpDecorate %_struct_6 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_10 = OpConstant %uint 10
|
||||
%_arr_int_uint_10 = OpTypeArray %int %uint_10
|
||||
%_struct_4 = OpTypeStruct %_arr_int_uint_10
|
||||
%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4
|
||||
%_struct_6 = OpTypeStruct %_runtimearr__struct_4
|
||||
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
|
||||
%_arr_int_uint_10_0 = OpTypeArray %int %uint_10
|
||||
%_struct_17 = OpTypeStruct %_arr_int_uint_10_0
|
||||
%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17
|
||||
%19 = OpTypeFunction %void %_ptr_Function__struct_17
|
||||
%2 = OpVariable %_ptr_Uniform__struct_6 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%20 = OpLabel
|
||||
%21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0
|
||||
%22 = OpFunctionCall %void %23 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%23 = OpFunction %void None %19
|
||||
%24 = OpFunctionParameter %_ptr_Function__struct_17
|
||||
%25 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersMissingMember) {
|
||||
// Validation should fail because the formal parameter type has two members,
|
||||
// while the actual parameter only has 1.
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||
OpMemberDecorate %_struct_5 0 Offset 0
|
||||
OpDecorate %_struct_5 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %int
|
||||
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_struct_15 = OpTypeStruct %int %int
|
||||
%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15
|
||||
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||
%18 = OpTypeFunction %void %_ptr_Function__struct_15
|
||||
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0
|
||||
%21 = OpFunctionCall %void %22 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%22 = OpFunction %void None %18
|
||||
%23 = OpFunctionParameter %_ptr_Function__struct_15
|
||||
%24 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument <id>"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersDifferentMemberType) {
|
||||
// Validation should fail because the formal parameter has a member that is
|
||||
// a different type than the actual parameter.
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||
OpMemberDecorate %_struct_5 0 Offset 0
|
||||
OpDecorate %_struct_5 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %uint
|
||||
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_struct_15 = OpTypeStruct %int
|
||||
%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15
|
||||
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||
%18 = OpTypeFunction %void %_ptr_Function__struct_15
|
||||
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0
|
||||
%21 = OpFunctionCall %void %22 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%22 = OpFunction %void None %18
|
||||
%23 = OpFunctionParameter %_ptr_Function__struct_15
|
||||
%24 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument <id>"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall,
|
||||
LogicallyMismatchedPointersIncompatableDecorations) {
|
||||
// Validation should fail because the formal parameter has an incompatible
|
||||
// decoration.
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||
OpMemberDecorate %_struct_5 0 Offset 0
|
||||
OpDecorate %_struct_5 Block
|
||||
OpMemberDecorate %_struct_15 0 NonWritable
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %int
|
||||
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||
%_ptr_StorageBuffer__struct_5 = OpTypePointer StorageBuffer %_struct_5
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_struct_15 = OpTypeStruct %int
|
||||
%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15
|
||||
%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3
|
||||
%18 = OpTypeFunction %void %_ptr_Function__struct_15
|
||||
%2 = OpVariable %_ptr_StorageBuffer__struct_5 StorageBuffer
|
||||
%1 = OpFunction %void None %14
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_StorageBuffer__struct_3 %2 %int_0 %uint_0
|
||||
%21 = OpFunctionCall %void %22 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%22 = OpFunction %void None %18
|
||||
%23 = OpFunctionParameter %_ptr_Function__struct_15
|
||||
%24 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument <id>"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall,
|
||||
LogicallyMismatchedPointersIncompatableDecorations2) {
|
||||
// Validation should fail because the formal parameter has an incompatible
|
||||
// decoration.
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpMemberDecorate %_struct_3 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_3 ArrayStride 4
|
||||
OpMemberDecorate %_struct_5 0 Offset 0
|
||||
OpDecorate %_struct_5 BufferBlock
|
||||
OpDecorate %_ptr_Uniform__struct_3 ArrayStride 4
|
||||
OpDecorate %_ptr_Uniform__struct_3_0 ArrayStride 8
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_struct_3 = OpTypeStruct %int
|
||||
%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
|
||||
%_struct_5 = OpTypeStruct %_runtimearr__struct_3
|
||||
%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
|
||||
%_ptr_Uniform__struct_3_0 = OpTypePointer Uniform %_struct_3
|
||||
%18 = OpTypeFunction %void %_ptr_Uniform__struct_3_0
|
||||
%2 = OpVariable %_ptr_Uniform__struct_5 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%19 = OpLabel
|
||||
%20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0
|
||||
%21 = OpFunctionCall %void %22 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%22 = OpFunction %void None %18
|
||||
%23 = OpFunctionParameter %_ptr_Uniform__struct_3_0
|
||||
%24 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument <id>"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersArraySize) {
|
||||
// Validation should fail because the formal parameter array has a different
|
||||
// number of element than the actual parameter.
|
||||
std::string spirv =
|
||||
R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %1 "main"
|
||||
OpExecutionMode %1 LocalSize 1 1 1
|
||||
OpSource HLSL 600
|
||||
OpDecorate %2 DescriptorSet 0
|
||||
OpDecorate %2 Binding 0
|
||||
OpDecorate %_arr_int_uint_10 ArrayStride 4
|
||||
OpMemberDecorate %_struct_4 0 Offset 0
|
||||
OpDecorate %_runtimearr__struct_4 ArrayStride 40
|
||||
OpMemberDecorate %_struct_6 0 Offset 0
|
||||
OpDecorate %_struct_6 BufferBlock
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_5 = OpConstant %uint 5
|
||||
%uint_10 = OpConstant %uint 10
|
||||
%_arr_int_uint_10 = OpTypeArray %int %uint_10
|
||||
%_struct_4 = OpTypeStruct %_arr_int_uint_10
|
||||
%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4
|
||||
%_struct_6 = OpTypeStruct %_runtimearr__struct_4
|
||||
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
|
||||
%void = OpTypeVoid
|
||||
%14 = OpTypeFunction %void
|
||||
%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
|
||||
%_arr_int_uint_5 = OpTypeArray %int %uint_5
|
||||
%_struct_17 = OpTypeStruct %_arr_int_uint_5
|
||||
%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17
|
||||
%19 = OpTypeFunction %void %_ptr_Function__struct_17
|
||||
%2 = OpVariable %_ptr_Uniform__struct_6 Uniform
|
||||
%1 = OpFunction %void None %14
|
||||
%20 = OpLabel
|
||||
%21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0
|
||||
%22 = OpFunctionCall %void %23 %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%23 = OpFunction %void None %19
|
||||
%24 = OpFunctionParameter %_ptr_Function__struct_17
|
||||
%25 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument <id>"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(StorageClass, ValidateFunctionCall,
|
||||
Values("UniformConstant", "Input", "Uniform", "Output",
|
||||
"Workgroup", "Private", "Function",
|
||||
|
||||
180
3rdparty/spirv-tools/test/val/val_image_test.cpp
vendored
180
3rdparty/spirv-tools/test/val/val_image_test.cpp
vendored
@@ -25,6 +25,7 @@ namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Not;
|
||||
|
||||
@@ -54,9 +55,33 @@ OpCapability ImageBuffer
|
||||
ss << "OpCapability SampledRect\n";
|
||||
}
|
||||
|
||||
// In 1.4, the entry point must list all module-scope variables used. Just
|
||||
// list all of them.
|
||||
std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" :
|
||||
R"(
|
||||
%uniform_image_f32_1d_0001
|
||||
%uniform_image_f32_1d_0002_rgba32f
|
||||
%uniform_image_f32_2d_0001
|
||||
%uniform_image_f32_2d_0010
|
||||
%uniform_image_u32_2d_0001
|
||||
%uniform_image_u32_2d_0000
|
||||
%uniform_image_s32_3d_0001
|
||||
%uniform_image_f32_2d_0002
|
||||
%uniform_image_s32_2d_0002
|
||||
%uniform_image_f32_spd_0002
|
||||
%uniform_image_f32_3d_0111
|
||||
%uniform_image_f32_cube_0101
|
||||
%uniform_image_f32_cube_0102_rgba32f
|
||||
%uniform_sampler
|
||||
%private_image_u32_buffer_0002_r32ui
|
||||
%private_image_u32_spd_0002
|
||||
%private_image_f32_buffer_0002_r32ui
|
||||
)";
|
||||
|
||||
ss << capabilities_and_extensions;
|
||||
ss << "OpMemoryModel Logical " << memory_model << "\n";
|
||||
ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
|
||||
ss << "OpEntryPoint " << execution_model
|
||||
<< " %main \"main\" " + interface_vars + "\n";
|
||||
if (execution_model == "Fragment") {
|
||||
ss << "OpExecutionMode %main OriginUpperLeft\n";
|
||||
}
|
||||
@@ -79,6 +104,8 @@ OpDecorate %uniform_image_s32_3d_0001 DescriptorSet 1
|
||||
OpDecorate %uniform_image_s32_3d_0001 Binding 2
|
||||
OpDecorate %uniform_image_f32_2d_0002 DescriptorSet 1
|
||||
OpDecorate %uniform_image_f32_2d_0002 Binding 3
|
||||
OpDecorate %uniform_image_s32_2d_0002 DescriptorSet 1
|
||||
OpDecorate %uniform_image_s32_2d_0002 Binding 4
|
||||
OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2
|
||||
OpDecorate %uniform_image_f32_spd_0002 Binding 0
|
||||
OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2
|
||||
@@ -222,6 +249,11 @@ OpDecorate %uniform_sampler Binding 0
|
||||
%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant
|
||||
%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002
|
||||
|
||||
%type_image_s32_2d_0002 = OpTypeImage %s32 2D 0 0 0 2 Unknown
|
||||
%ptr_image_s32_2d_0002 = OpTypePointer UniformConstant %type_image_s32_2d_0002
|
||||
%uniform_image_s32_2d_0002 = OpVariable %ptr_image_s32_2d_0002 UniformConstant
|
||||
%type_sampled_image_s32_2d_0002 = OpTypeSampledImage %type_image_s32_2d_0002
|
||||
|
||||
%type_image_f32_spd_0002 = OpTypeImage %f32 SubpassData 0 0 0 2 Unknown
|
||||
%ptr_image_f32_spd_0002 = OpTypePointer UniformConstant %type_image_f32_spd_0002
|
||||
%uniform_image_f32_spd_0002 = OpVariable %ptr_image_f32_spd_0002 UniformConstant
|
||||
@@ -703,7 +735,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a "
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 140[%140] cannot be a "
|
||||
"type"));
|
||||
}
|
||||
|
||||
@@ -4571,6 +4603,150 @@ TEST_F(ValidateImage, Issue2463NoSegFault) {
|
||||
"for OpReturnValue"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendV13Bad) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment",
|
||||
SPV_ENV_UNIVERSAL_1_3)),
|
||||
HasSubstr("Invalid image operand 'SignExtend'"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendV13Bad) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
|
||||
EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment",
|
||||
SPV_ENV_UNIVERSAL_1_3)),
|
||||
HasSubstr("Invalid image operand 'ZeroExtend'"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) {
|
||||
// Unsigned int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarSIntTexelV14Good) {
|
||||
// Signed int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendScalarVectorUIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, SignExtendVectorSIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32vec4 %img %u32vec2_01 SignExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
// No negative tests for SignExtend since we don't truly know the
|
||||
// texel format.
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarUIntTexelV14Good) {
|
||||
// Unsigned int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) {
|
||||
// Zeroed int sampled type
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000
|
||||
%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) {
|
||||
const std::string body = R"(
|
||||
%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
|
||||
%res1 = OpImageRead %s32vec4 %img %u32vec2_01 ZeroExtend
|
||||
)";
|
||||
const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
|
||||
|
||||
CompileSuccessfully(
|
||||
GenerateShaderCode(body, extra, "Fragment", SPV_ENV_UNIVERSAL_1_4),
|
||||
SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
// No negative tests for ZeroExtend since we don't truly know the
|
||||
// texel format.
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -48,8 +48,9 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, EntryPointMissingOutput) {
|
||||
@@ -74,8 +75,9 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Output variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, InterfaceMissingUseInSubfunction) {
|
||||
@@ -105,8 +107,9 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <5> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, TwoEntryPointsOneFunction) {
|
||||
@@ -132,8 +135,9 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <2> is used by entry point 'func2' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <2> is used by entry point 'func2' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingInterfaceThroughInitializer) {
|
||||
@@ -160,8 +164,239 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Input variable id <6> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
HasSubstr(
|
||||
"Interface variable id <6> is used by entry point 'func' id <1>, "
|
||||
"but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Input %struct
|
||||
%var = OpVariable %ptr_struct Input
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %main "main"
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Input %struct
|
||||
%var = OpVariable %ptr_struct Input
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Non-unique OpEntryPoint interface 2[%var] is disallowed"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Interface variable id <2> is used by entry point "
|
||||
"'main' id <1>, but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Function %struct
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%var = OpVariable %ptr_struct Function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpEntryPoint interfaces must be OpVariables with "
|
||||
"Storage Class of Input(1) or Output(3). Found Storage "
|
||||
"Class 7 for Entry Point id 1."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer Function %struct
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%var = OpVariable %ptr_struct Function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpEntryPoint interfaces should only list global variables"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_NotAllUsedGlobals) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint3 = OpTypeVector %uint 3
|
||||
%struct = OpTypeStruct %uint3
|
||||
%ptr_struct = OpTypePointer StorageBuffer %struct
|
||||
%var = OpVariable %ptr_struct StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%main = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_DuplicateInterface) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %gid %gid
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %gid BuiltIn GlobalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int3 = OpTypeVector %int 3
|
||||
%ptr_input_int3 = OpTypePointer Input %int3
|
||||
%gid = OpVariable %ptr_input_int3 Input
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, SPV14MultipleEntryPointsSameFunction) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main1" %gid
|
||||
OpEntryPoint GLCompute %main "main2" %gid
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %gid BuiltIn GlobalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int3 = OpTypeVector %int 3
|
||||
%ptr_input_int3 = OpTypePointer Input %int3
|
||||
%gid = OpVariable %ptr_input_int3 Input
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -754,13 +754,17 @@ TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) {
|
||||
OpName %loop "loop"
|
||||
OpName %exit "exit"
|
||||
%voidt = OpTypeVoid
|
||||
%boolt = OpTypeBool
|
||||
%undef = OpUndef %boolt
|
||||
%funct = OpTypeFunction %voidt
|
||||
%main = OpFunction %voidt None %funct
|
||||
%entry = OpLabel
|
||||
OpBranch %exit
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %loop %loop None
|
||||
OpBranch %loop
|
||||
OpLoopMerge %dead %loop None
|
||||
OpBranchConditional %undef %loop %loop
|
||||
%dead = OpLabel
|
||||
OpUnreachable
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
@@ -85,6 +85,16 @@ TEST_F(ValidateLiterals, LiteralsShaderGood) {
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateLiterals, InvalidInt) {
|
||||
std::string str = GenerateShaderCode() + R"(
|
||||
%11 = OpTypeInt 32 90
|
||||
)";
|
||||
CompileSuccessfully(str);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpTypeInt has invalid signedness:"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateLiteralsShader, LiteralsShaderBad) {
|
||||
std::string str = GenerateShaderCode() + GetParam();
|
||||
std::string inst_id = "11";
|
||||
|
||||
210
3rdparty/spirv-tools/test/val/val_logicals_test.cpp
vendored
210
3rdparty/spirv-tools/test/val/val_logicals_test.cpp
vendored
@@ -24,6 +24,7 @@ namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Not;
|
||||
|
||||
@@ -144,6 +145,18 @@ OpExecutionMode %main OriginUpperLeft
|
||||
%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true
|
||||
%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false
|
||||
|
||||
%arr_u32_2 = OpTypeArray %u32 %u32_2
|
||||
%st_u32_u32 = OpTypeStruct %u32 %u32
|
||||
%mat_f32_2_2 = OpTypeMatrix %f32vec2 2
|
||||
|
||||
%nul_arr_u32_2 = OpConstantNull %arr_u32_2
|
||||
%nul_st_u32_u32 = OpConstantNull %st_u32_u32
|
||||
%nul_mat_f32_2_2 = OpConstantNull %mat_f32_2_2
|
||||
|
||||
%arr_u32_2_1_2 = OpConstantComposite %arr_u32_2 %u32_1 %u32_2
|
||||
%st_u32_u32_1_2 = OpConstantComposite %st_u32_u32 %u32_1 %u32_2
|
||||
%mat_f32_2_2_01_12 = OpConstantComposite %mat_f32_2_2 %f32vec2_01 %f32vec2_12
|
||||
|
||||
%f32vec4ptr = OpTypePointer Function %f32vec4
|
||||
|
||||
%main = OpFunction %void None %func
|
||||
@@ -585,6 +598,20 @@ TEST_F(ValidateLogicals, OpSelectWrongTypeId) {
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectWrongTypeIdV14) {
|
||||
// In 1.4, the message changes to allow composites.
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %void %true %u32_0 %u32_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or composite type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectPointerNoCapability) {
|
||||
const std::string body = R"(
|
||||
%x = OpVariable %f32vec4ptr Function
|
||||
@@ -687,6 +714,111 @@ TEST_F(ValidateLogicals, OpSelectWrongRightObject) {
|
||||
HasSubstr("Expected both objects to be of Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectArrayV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectStructV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV13Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV13TargetV14Bad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected scalar or vector type as Result Type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpSelectMatrixV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, OpIEqualSuccess) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpIEqual %bool %u32_0 %s32_1
|
||||
@@ -949,6 +1081,84 @@ OpFunctionEnd
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsScalarCondition) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%true = OpConstantTrue %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %true %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected vector sizes of Result Type and the "
|
||||
"condition to be equal: Select"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsScalarCondition1p4) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%true = OpConstantTrue %bool
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %true %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLogicals, SelectVectorsVectorConditionMismatchedDimensions1p4) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%bool3 = OpTypeVector %bool 3
|
||||
%int = OpTypeInt 32 0
|
||||
%int4 = OpTypeVector %int 4
|
||||
%int4_0 = OpConstantNull %int4
|
||||
%bool3_null = OpConstantNull %bool3
|
||||
%void_fn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_fn
|
||||
%1 = OpLabel
|
||||
%select = OpSelect %int4 %bool3_null %int4_0 %int4_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected vector sizes of Result Type and the "
|
||||
"condition to be equal: Select"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
665
3rdparty/spirv-tools/test/val/val_memory_test.cpp
vendored
665
3rdparty/spirv-tools/test/val/val_memory_test.cpp
vendored
@@ -27,6 +27,7 @@ namespace {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Values;
|
||||
|
||||
using ValidateMemory = spvtest::ValidateBase<bool>;
|
||||
|
||||
@@ -1342,32 +1343,6 @@ OpFunctionEnd
|
||||
"VulkanMemoryModelDeviceScopeKHR capability"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood1) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood2) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@@ -1422,6 +1397,138 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisBadBinaryV13) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"with two memory access operands requires SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessFirstWithAvBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
MakePointerAvailableKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Source memory access must not include MakePointerAvailableKHR\n"
|
||||
" OpCopyMemory %5 %6 MakePointerAvailableKHR|NonPrivatePointerKHR"
|
||||
" %uint_1 MakePointerAvailableKHR|NonPrivatePointerKHR %uint_1"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessSecondWithVisBad) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%device = OpConstant %int 1
|
||||
%int_ptr_ssbo = OpTypePointer StorageBuffer %int
|
||||
%var1 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%var2 = OpVariable %int_ptr_ssbo StorageBuffer
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
MakePointerVisibleKHR|NonPrivatePointerKHR %device
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Target memory access must not include MakePointerVisibleKHR\n"
|
||||
" OpCopyMemory %5 %6 MakePointerVisibleKHR|NonPrivatePointerKHR"
|
||||
" %uint_1 MakePointerVisibleKHR|NonPrivatePointerKHR %uint_1"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad1) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@@ -1767,8 +1874,8 @@ OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(body.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
CompileSuccessfully(body);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable"));
|
||||
@@ -2934,6 +3041,508 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemoryNoAccessGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleMixedAccessGood) {
|
||||
// Test one memory access operand using features that don't require the
|
||||
// Vulkan memory model.
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile|Aligned|Nontemporal 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV13Bad) {
|
||||
// Two memory access operands is invalid up to SPIR-V 1.3
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("CopyMemory with two memory access operands requires "
|
||||
"SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV14Good) {
|
||||
// Two memory access operands is valid in SPIR-V 1.4
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemory %var1 %var2 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedNoAccessGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleMixedAccessGood) {
|
||||
// Test one memory access operand using features that don't require the
|
||||
// Vulkan memory model.
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile|Aligned|Nontemporal 4
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV13Bad) {
|
||||
// Two memory access operands is invalid up to SPIR-V 1.3
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("CopyMemorySized with two memory access operands requires "
|
||||
"SPIR-V 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV14Good) {
|
||||
// Two memory access operands is valid in SPIR-V 1.4
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Addresses
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_16 = OpConstant %int 16
|
||||
%int_ptr_priv = OpTypePointer Private %int
|
||||
%var1 = OpVariable %int_ptr_priv Private
|
||||
%var2 = OpVariable %int_ptr_priv Private
|
||||
%voidfn = OpTypeFunction %void
|
||||
%func = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
using ValidatePointerComparisons = spvtest::ValidateBase<std::string>;
|
||||
|
||||
TEST_P(ValidatePointerComparisons, Good) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, GoodWorkgroup) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointers
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Workgroup %int
|
||||
%var = OpVariable %ptr_int Workgroup
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadResultType) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %bool ";
|
||||
} else {
|
||||
spirv += " %int ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
if (operation == "OpPtrDiff") {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must be an integer scalar"));
|
||||
} else {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Result Type must be OpTypeBool"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadCapabilities) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
if (operation == "OpPtrDiff") {
|
||||
// Gets caught by the grammar.
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
} else {
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Instruction cannot be used without a variable "
|
||||
"pointers capability"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadOperandType) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer StorageBuffer %int
|
||||
%var = OpVariable %ptr_int StorageBuffer
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %int %var
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%ld %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Operand type must be a pointer"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadStorageClassWorkgroup) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Workgroup %int
|
||||
%var = OpVariable %ptr_int Workgroup
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Workgroup storage class pointer requires "
|
||||
"VariablePointers capability to be specified"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadStorageClass) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Private %int
|
||||
%var = OpVariable %ptr_int Private
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Invalid pointer storage class"));
|
||||
}
|
||||
|
||||
TEST_P(ValidatePointerComparisons, BadDiffOperandTypes) {
|
||||
const std::string operation = GetParam();
|
||||
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability VariablePointersStorageBuffer
|
||||
OpMemoryModel Logical GLSL450
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr_int = OpTypePointer Private %int
|
||||
%var = OpVariable %ptr_int Private
|
||||
%func_ty = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_ty
|
||||
%1 = OpLabel
|
||||
%ld = OpLoad %int %var
|
||||
%equal = )" + operation;
|
||||
|
||||
if (operation == "OpPtrDiff") {
|
||||
spirv += " %int ";
|
||||
} else {
|
||||
spirv += " %bool ";
|
||||
}
|
||||
|
||||
spirv += R"(%var %ld
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("The types of Operand 1 and Operand 2 must match"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PointerComparisons, ValidatePointerComparisons,
|
||||
Values("OpPtrEqual", "OpPtrNotEqual", "OpPtrDiff"));
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
||||
61
3rdparty/spirv-tools/test/val/val_opencl_test.cpp
vendored
Normal file
61
3rdparty/spirv-tools/test/val/val_opencl_test.cpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2019 The Khronos Group Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Validation tests for OpenCL env specific checks
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
using testing::HasSubstr;
|
||||
|
||||
using ValidateOpenCL = spvtest::ValidateBase<bool>;
|
||||
|
||||
TEST_F(ValidateOpenCL, NonPhysicalAddressingModelBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpMemoryModel Logical OpenCL
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Addressing model must be Physical32 or Physical64 "
|
||||
"in the OpenCL environment.\n OpMemoryModel Logical "
|
||||
"OpenCL\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpMemoryModel Physical32 GLSL450
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Memory model must be OpenCL in the OpenCL environment."
|
||||
"\n OpMemoryModel Physical32 GLSL450\n"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
@@ -213,7 +213,7 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParam) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str);
|
||||
getValidatorOptions()->relax_logical_pointer = true;
|
||||
getValidatorOptions()->before_hlsl_legalization = true;
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user