Update SPIR-V Tools to 8a5500656
Changes:
8a5500656 spirv-fuzz: adds TransformationReplaceLoadStoreWithCopyMemory (#3586)
7c901a49c Preserve OpenCL.DebugInfo.100 through private-to-local pass (#3571)
767518e8e spirv-fuzz: Relax type checking for int contants (#3573)
f8920bcfa spirv-fuzz: Generalise transformation access chain (#3546)
98ac9fd6d spirv-fuzz: Split blocks starting with OpPhi before trying to outline (#3581)
059ab0819 spirv-fuzz: Set message consumer in replayer when shrinking (#3591)
d6306537d spirv-fuzz: Don't use default parameters (#3583)
969f02864 Change DEPS rolling script to point at external/ (#3584)
1aaf5c613 spirv-fuzz: Create a helper in fuzzerutil to reuse function type (#3572)
89b3bc5a8 spirv-fuzz: Test usages of IdIsIrrelevant fact (#3578)
9dc1bfa31 spirv-fuzz: adds TransformationReplaceCopyMemoryWithLoadStore (#3575)
586a12b9d spirv-fuzz: adds TransformationReplaceCopyObjectWithStoreLoad (#3567)
0b84727f2 Start SPIRV-Tools v2020.5
895927bd3 Finalize SPIRV-Tools v2020.4
bc2f78b7d spirv-fuzz: Fix usages of irrelevant constants (#3566)
a0d8dc2b4 Update CHANGES
f3cec9366 Support SPV_KHR_terminate_invocation (#3568)
dca2c86bc Sink pointer instructions in merge return (#3569)
cf7e922e7 Preserve OpenCL.DebugInfo.100 through elim-dead-code-aggressive (#3542)
fe9e5db89 spirv-fuzz: TransformationReplaceParamsWithStruct (#3455)
e4aebf99f Add changes for SPV_EXT_shader_atomic_float (#3562)
5dc96d5d2 spirv-fuzz: Use irrelevant constants (#3565)
98395b834 spirv-fuzz: Extend TransformationRecordSynonymousConstants to allow composite constants (#3537)
a3b0adc30 spirv-fuzz: Add is_irrelevant parameter (#3563)
8b5ed4448 spirv-fuzz: Add IdIsIrrelevant fact (#3561)
c10d6cebb spirv-fuzz: refactor to use RemoveAtRandomIndex (#3560)
0d8fe0fba spirv-fuzz: add TransformationAddRelaxedDecoration (#3545)
717e7877c Store location values sparsely (#3488)
d5766f280 Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463)
bcc78b3e9 spirv-fuzz: support floating-point in TransformationInvertComparisonOperator (#3551)
fb32c4074 Change MaybeApplyTransformation to return a boolean (#3555)
7dfd9b868 spirv-fuzz: Implement MaybeApplyTransformation helper function (#3540)
de1ff50f2 spirv-fuzz: Assert false in IsApplicable method of TransformationAccessChain (#3528)
680c77fa6 spirv-fuzz: Add support for OpBitcast to TransformationEquationInstruction (#3523)
b0206b0f6 spirv-fuzz: Add support for OpConvert to TransformationEquationInstruction (#3472)
7221ccf85 Fix reachability in the validator (#3541)
2fa735dc0 spirv-fuzz: Remove TransformationCopyObject (#3531)
8e0215afe spirv-opt: Add support for OpLabel to dominator analysis (#3516)
f12c40f5a spirv-fuzz: Fuzzer pass to interchange zero-like constants (#3524)
3e7238c68 spirv-fuzz: Add replay range option (#3535)
4c33fb0d3 Rewrite KillDebugDeclares() (#3513)
362276978 spirv-fuzz: Fix instruction insertion issue (#3521)
91d921e89 spirv-fuzz: Implement the OpMatrixTimesMatrix linear algebra case (#3527)
282392dda Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512)
c9b254d04 spirv-fuzz: Support adding dead break from back-edge block (#3519)
fe4dca516 Support OpPhi when replacing boolean constant operand (#3518)
40c3c1cac spirv-fuzz: TransformationAddSynonyms (#3447)
13dc28ce7 spirv-fuzz: Remove unused functions (#3510)
282962362 spirv-fuzz: Minor refactoring (#3507)
cf8c86a2d Preserve OpenCL.DebugInfo.100 through elim-local-single-store (#3498)
a687057a8 Preserve debug info in vector DCE pass (#3497)
5f8cdd8b4 Implement transformation to record synonymous constants. (#3494)
94667fbf6 Fix build failure (#3508)
44428352b Upgrade elim-local-single-block for OpenCL.DebugInfo.100 (#3451)
de56c34bd spirv-fuzz: TransformationReplaceParameterWithGlobal (#3434)
11946e640 Implement the OpMatrixTimesVector linear algebra case (#3500)
f8eddbbe5 Preserve OpenCL.100.DebugInfo in reduce-load-size pass (#3492)
daa3b47ed spirv-fuzz: Add image sample unused components transformation (#3439)
7afbc0c8b spirv-fuzz: Add variables with workgroup storage class (#3485)
2fbeb04b6 spirv-fuzz: Implement the OpVectorTimesMatrix linear algebra case (#3489)
6d61c1159 spirv-fuzz: fuzzerutil::MaybeGetConstant* #3487
6a4da9da4 Debug info preservation in copy-prop-array pass (#3444)
bd2a9ea85 spirv-fuzz: TransformationInvertComparisonOperator (#3475)
2c1ff230c Fix regression (#3481)
4a92579a4 spirv-fuzz: Add fuzzerutil::FindOrCreate* (#3479)
fba90d6b0 spirv-fuzz: Add FuzzerPassAddCopyMemoryInstructions (#3391)
ab10489a0 spirv-fuzz: Add one parameter at a time (#3469)
fc0dc3a9c Fix ADCE pass bug for mulitple entries (#3470)
91c50e3fc Add gl_BaseInstance to the name mapper. (#3462)
c3680adbd Implement the OpMatrixTimesScalar linear algebra case (#3450)
efaae24d0 Clear debug information for kill and replacement (#3459)
a1fb255a2 Validate location assignments (#3308)
7a1af5878 Support OpCompositeExtract pattern in desc_sroa (#3456)
29ba53f2a spirv-fuzz: Implement FuzzerPassAddParameters (#3399)
71a5b6770 spirv-fuzz: Add GetParameters (#3454)
8e586e46a spirv-fuzz: Permute OpPhi instruction operands (#3421)
36b5bb701 Add support for different default/trunks in roll-deps (#3442)
d4b9f576e [spirv-opt] debug info preservation in ssa-rewrite (#3356)
2a1b8c062 Updated desc_sroa to support flattening structures (#3448)
33cf7c425 spirv-fuzz: Refactor variable creation (#3414)
d5306c8e8 spirv-fuzz: Swap operands in OpBranchConditional (#3423)
545d158a2 Use structured order to unroll loops. (#3443)
5342930f9 Debug info preservation in dead branch elimination (#3425)
99651228b Add RemoveParameter method (#3437)
57d9e360c Fix return type (#3435)
a7112d544 Eliminate branches with condition of OpConstantNull (#3438)
949470354 spirv-fuzz: Implement vector shuffle fuzzer pass (#3412)
12a4fb3bc spirv-fuzz: Add replace linear algebra instruction transformation (#3402)
52a5f074e Update access control lists. (#3433)
30bf46dbe Fix operand access (#3427)
5543d5faa Debug info preservation in ccp pass (#3420)
458140aed Fix round trip tests that weren't instantiated (#3417)
e49896709 spirv-fuzz: Add a test (#3238)
c01f826bc spirv-fuzz: Add support for OpSpecConstant* (#3373)
7c213720b spirv-fuzz: Fix replayer bug (#3401)
9ed0fef6e Add value instruction condition (#3385)
ffaecad32 Fix instruction function use (#3390)
94808bd0f spirv-fuzz: Fix regression (#3396)
74130f2d3 Fix googletest inclusion (#3398)
636f449e1 Add tests for merge-return debug info preservation (#3389)
f050cca7e spirv-fuzz: Add push id through variable transformation (#3359)
8dfdbeff8 Rolling 4 dependencies (#3380)
20e7fc505 Start SPIRV-Tools v2020.4
e128ab0d6 Finalize SPIRV-Tools v2020.3
9b8a6a144 Update CHANGES
d3d89bb90 spirv-fuzz: Support bit width argument for int and float types (#3378)
Commands:
./third_party/update-spirvtools.sh
Bug: b/123642959
Change-Id: Id54668de78b22bf61434d050162e06de30366d52
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES
index fe6641e..d10f55b 100644
--- a/third_party/SPIRV-Tools/CHANGES
+++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,7 +1,77 @@
Revision history for SPIRV-Tools
-v2020.3-dev 2020-03-26
- - Start v2020.3-dev
+v2020.5 2020-07-22
+ - Start SPIRV-Tools v2020.5
+
+v2020.4 2020-07-22
+ - General
+ - Changed variable names to be more descriptive (#3433)
+ - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512)
+ - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463)
+ - Support SPV_KHR_terminate_invocation (#3568)
+ - Optimizer
+ - Preserving debug information in optimizations
+ (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542)
+ - Eliminate branches with condition of OpConstantNull (#3438)
+ - Use structured order to unroll loops. (#3443)
+ - Updated desc_sroa to support flattening structures (#3448)
+ - Support OpCompositeExtract pattern in desc_sroa (#3456)
+ - Fix ADCE pass bug for mulitple entries (#3470)
+ - Sink pointer instructions in merge return (#3569)
+ - Validator
+ - Validate location assignments (#3308)
+ - Fix reachability in the validator (#3541)
+ - Reduce
+ - Fuzz
+ - Add support for OpSpecConstant* (#3373)
+ - Add replace linear algebra instruction transformation (#3402)
+ - Implement vector shuffle fuzzer pass (#3412)
+ - Swap operands in OpBranchConditional (#3423)
+ - Permute OpPhi instruction operands (#3421)
+ - Add FuzzerPassAddCopyMemoryInstructions (#3391)
+ - TransformationInvertComparisonOperator (#3475)
+ - Add variables with workgroup storage class (#3485)
+ - Add image sample unused components transformation (#3439)
+ - TransformationReplaceParameterWithGlobal (#3434)
+ - Support adding dead break from back-edge block (#3519)
+ - Fuzzer pass to interchange zero-like constants (#3524)
+ - Linker
+
+v2020.3 2020-05-27
+ - General
+ - Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
+ - Update acorn version (#3294)
+ - If SPIRV-Headers is in our tree, include it as subproject (#3299)
+ - allow cross compiling for Windows Store, UWP, etc. (#3330)
+ - Optimizer
+ - Remove deprecated interfaces from instrument passes (#3361)
+ - Preserve debug info in inline pass (#3349)
+ - Handle more cases in dead member elim (#3289)
+ - Preserve debug info in eliminate-dead-functions (#3251)
+ - Fix Struct CFG analysis for single block loop (#3293)
+ - Add tests for recently added command line option (#3297)
+ - Consider sampled images as read-only storage (#3295)
+ - Allow various validation options to be passed to spirv-opt (#3314)
+ - Add debug information analysis (#3305)
+ - Preserve debug info for wrap-opkill (#3331)
+ - refactor inlining pass (#3328)
+ - Add unrolling to performance passes (#3082)
+ - Validator
+ - Add validation support for ImageGatherBiasLodAMD (#3363)
+ - Validate ShaderCallKHR memory scope (#3332)
+ - Validate Buffer and BufferBlock apply only to struct types (#3259)
+ - Reduce
+ - increase default step limit (#3327)
+ - Remove unused uniforms and similar (#3321)
+ - Fuzz
+ - Add support for StorageBuffer (#3348)
+ - Add validator options (#3254)
+ - Limit adding of new variables to 'basic' types (#3257)
+ - Transformation to add OpConstantNull (#3273)
+ - Handling of more fuzzing opportunities (#3277, #3280, #3281, #3290, #3292)
+ - Respect rules for OpSampledImage (#3287)
+ - Do not outline regions that produce pointer outputs (#3291)
+ - Linker
v2020.2 2020-03-26
- General:
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index c3e78a2..a27b921 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -3,10 +3,10 @@
vars = {
'github': 'https://github.com',
- 'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
- 'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
- 're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
- 'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0',
+ 'effcee_revision': '5af957bbfc7da4e9f7aa8cac11379fa36dd79b84',
+ 'googletest_revision': '011959aafddcd30611003de96cfd8d7a7685c700',
+ 're2_revision': 'aecba11114cf1fac5497aeb844b6966106de3eb6',
+ 'spirv_headers_revision': 'ac638f1815425403e946d0ab78bac71d2bdbf3be',
}
deps = {
diff --git a/third_party/SPIRV-Tools/external/CMakeLists.txt b/third_party/SPIRV-Tools/external/CMakeLists.txt
index 56dd54f..5b34159 100644
--- a/third_party/SPIRV-Tools/external/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/external/CMakeLists.txt
@@ -47,7 +47,7 @@
if (TARGET gmock)
message(STATUS "Google Mock already configured")
else()
- set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googlemock)
+ set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
if(EXISTS ${GMOCK_DIR})
if(MSVC)
# Our tests use ::testing::Combine. Work around a compiler
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
index ef5136a..b4f3355 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -110,6 +110,16 @@
static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
+// Mesh Shader Output Record Offsets
+static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
+// Task Shader Output Record Offsets
+static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
// Size of Common and Stage-specific Members
static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
index 03c7d1b..68afd64 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -676,6 +676,12 @@
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
spv_fuzzer_options options, uint32_t seed);
+// Sets the range of transformations that should be applied during replay: 0
+// means all transformations, +N means the first N transformations, -N means all
+// except the final N transformations.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+ spv_fuzzer_options options, int32_t replay_range);
+
// Sets the maximum number of steps that the shrinker should take before giving
// up.
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
index 5e1819e..6b31a07 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp
@@ -227,6 +227,11 @@
spvFuzzerOptionsSetRandomSeed(options_, seed);
}
+ // See spvFuzzerOptionsSetReplayRange.
+ void set_replay_range(int32_t replay_range) {
+ spvFuzzerOptionsSetReplayRange(options_, replay_range);
+ }
+
// See spvFuzzerOptionsSetShrinkerStepLimit.
void set_shrinker_step_limit(uint32_t shrinker_step_limit) {
spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index d393495..741f947 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -876,8 +876,10 @@
// for the first index.
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
-// Create a pass to replace all OpKill instruction with a function call to a
-// function that has a single OpKill. This allows more code to be inlined.
+// Create a pass to replace each OpKill instruction with a function call to a
+// function that has a single OpKill. Also replace each OpTerminateInvocation
+// instruction with a function call to a function that has a single
+// OpTerminateInvocation. This allows more code to be inlined.
Optimizer::PassToken CreateWrapOpKillPass();
// Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index 6582927..c413614 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -39,16 +39,22 @@
fuzzer_pass.h
fuzzer_pass_add_access_chains.h
fuzzer_pass_add_composite_types.h
+ fuzzer_pass_add_copy_memory.h
fuzzer_pass_add_dead_blocks.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
fuzzer_pass_add_equation_instructions.h
fuzzer_pass_add_function_calls.h
fuzzer_pass_add_global_variables.h
+ fuzzer_pass_add_image_sample_unused_components.h
+ fuzzer_pass_add_synonyms.h
fuzzer_pass_add_loads.h
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h
+ fuzzer_pass_add_parameters.h
+ fuzzer_pass_add_relaxed_decorations.h
fuzzer_pass_add_stores.h
+ fuzzer_pass_add_vector_shuffle_instructions.h
fuzzer_pass_adjust_branch_weights.h
fuzzer_pass_adjust_function_controls.h
fuzzer_pass_adjust_loop_controls.h
@@ -58,13 +64,24 @@
fuzzer_pass_construct_composites.h
fuzzer_pass_copy_objects.h
fuzzer_pass_donate_modules.h
+ fuzzer_pass_invert_comparison_operators.h
+ fuzzer_pass_interchange_zero_like_constants.h
fuzzer_pass_merge_blocks.h
fuzzer_pass_obfuscate_constants.h
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_permute_function_parameters.h
+ fuzzer_pass_permute_phi_operands.h
+ fuzzer_pass_push_ids_through_variables.h
+ fuzzer_pass_replace_copy_memories_with_loads_stores.h
+ fuzzer_pass_replace_copy_objects_with_stores_loads.h
+ fuzzer_pass_replace_linear_algebra_instructions.h
+ fuzzer_pass_replace_loads_stores_with_copy_memories.h
+ fuzzer_pass_replace_parameter_with_global.h
+ fuzzer_pass_replace_params_with_struct.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
+ fuzzer_pass_swap_conditional_branch_operands.h
fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_util.h
id_use_descriptor.h
@@ -81,14 +98,20 @@
transformation_add_constant_composite.h
transformation_add_constant_null.h
transformation_add_constant_scalar.h
+ transformation_add_copy_memory.h
transformation_add_dead_block.h
transformation_add_dead_break.h
transformation_add_dead_continue.h
transformation_add_function.h
transformation_add_global_undef.h
transformation_add_global_variable.h
+ transformation_add_image_sample_unused_components.h
transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h
+ transformation_add_parameter.h
+ transformation_add_relaxed_decoration.h
+ transformation_add_spec_constant_op.h
+ transformation_add_synonym.h
transformation_add_type_array.h
transformation_add_type_boolean.h
transformation_add_type_float.h
@@ -103,17 +126,26 @@
transformation_composite_extract.h
transformation_compute_data_synonym_fact_closure.h
transformation_context.h
- transformation_copy_object.h
transformation_equation_instruction.h
transformation_function_call.h
+ transformation_invert_comparison_operator.h
transformation_load.h
transformation_merge_blocks.h
transformation_move_block_down.h
transformation_outline_function.h
transformation_permute_function_parameters.h
+ transformation_permute_phi_operands.h
+ transformation_push_id_through_variable.h
+ transformation_record_synonymous_constants.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
+ transformation_replace_copy_memory_with_load_store.h
+ transformation_replace_copy_object_with_store_load.h
transformation_replace_id_with_synonym.h
+ transformation_replace_linear_algebra_instruction.h
+ transformation_replace_load_store_with_copy_memory.h
+ transformation_replace_parameter_with_global.h
+ transformation_replace_params_with_struct.h
transformation_set_function_control.h
transformation_set_loop_control.h
transformation_set_memory_operands_mask.h
@@ -121,6 +153,7 @@
transformation_split_block.h
transformation_store.h
transformation_swap_commutable_operands.h
+ transformation_swap_conditional_branch_operands.h
transformation_toggle_access_chain_instruction.h
transformation_vector_shuffle.h
uniform_buffer_element_descriptor.h
@@ -135,16 +168,22 @@
fuzzer_pass.cpp
fuzzer_pass_add_access_chains.cpp
fuzzer_pass_add_composite_types.cpp
+ fuzzer_pass_add_copy_memory.cpp
fuzzer_pass_add_dead_blocks.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
fuzzer_pass_add_equation_instructions.cpp
fuzzer_pass_add_function_calls.cpp
fuzzer_pass_add_global_variables.cpp
+ fuzzer_pass_add_image_sample_unused_components.cpp
+ fuzzer_pass_add_synonyms.cpp
fuzzer_pass_add_loads.cpp
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
+ fuzzer_pass_add_parameters.cpp
+ fuzzer_pass_add_relaxed_decorations.cpp
fuzzer_pass_add_stores.cpp
+ fuzzer_pass_add_vector_shuffle_instructions.cpp
fuzzer_pass_adjust_branch_weights.cpp
fuzzer_pass_adjust_function_controls.cpp
fuzzer_pass_adjust_loop_controls.cpp
@@ -154,13 +193,24 @@
fuzzer_pass_construct_composites.cpp
fuzzer_pass_copy_objects.cpp
fuzzer_pass_donate_modules.cpp
+ fuzzer_pass_invert_comparison_operators.cpp
+ fuzzer_pass_interchange_zero_like_constants.cpp
fuzzer_pass_merge_blocks.cpp
fuzzer_pass_obfuscate_constants.cpp
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_permute_function_parameters.cpp
+ fuzzer_pass_permute_phi_operands.cpp
+ fuzzer_pass_push_ids_through_variables.cpp
+ fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+ fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
+ fuzzer_pass_replace_linear_algebra_instructions.cpp
+ fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
+ fuzzer_pass_replace_parameter_with_global.cpp
+ fuzzer_pass_replace_params_with_struct.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
+ fuzzer_pass_swap_conditional_branch_operands.cpp
fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
@@ -176,14 +226,20 @@
transformation_add_constant_composite.cpp
transformation_add_constant_null.cpp
transformation_add_constant_scalar.cpp
+ transformation_add_copy_memory.cpp
transformation_add_dead_block.cpp
transformation_add_dead_break.cpp
transformation_add_dead_continue.cpp
transformation_add_function.cpp
transformation_add_global_undef.cpp
transformation_add_global_variable.cpp
+ transformation_add_image_sample_unused_components.cpp
transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp
+ transformation_add_parameter.cpp
+ transformation_add_relaxed_decoration.cpp
+ transformation_add_spec_constant_op.cpp
+ transformation_add_synonym.cpp
transformation_add_type_array.cpp
transformation_add_type_boolean.cpp
transformation_add_type_float.cpp
@@ -198,17 +254,26 @@
transformation_composite_extract.cpp
transformation_compute_data_synonym_fact_closure.cpp
transformation_context.cpp
- transformation_copy_object.cpp
transformation_equation_instruction.cpp
transformation_function_call.cpp
+ transformation_invert_comparison_operator.cpp
transformation_load.cpp
transformation_merge_blocks.cpp
transformation_move_block_down.cpp
transformation_outline_function.cpp
transformation_permute_function_parameters.cpp
+ transformation_permute_phi_operands.cpp
+ transformation_push_id_through_variable.cpp
+ transformation_record_synonymous_constants.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
+ transformation_replace_copy_memory_with_load_store.cpp
+ transformation_replace_copy_object_with_store_load.cpp
transformation_replace_id_with_synonym.cpp
+ transformation_replace_linear_algebra_instruction.cpp
+ transformation_replace_load_store_with_copy_memory.cpp
+ transformation_replace_parameter_with_global.cpp
+ transformation_replace_params_with_struct.cpp
transformation_set_function_control.cpp
transformation_set_loop_control.cpp
transformation_set_memory_operands_mask.cpp
@@ -216,6 +281,7 @@
transformation_split_block.cpp
transformation_store.cpp
transformation_swap_commutable_operands.cpp
+ transformation_swap_conditional_branch_operands.cpp
transformation_toggle_access_chain_instruction.cpp
transformation_vector_shuffle.cpp
uniform_buffer_element_descriptor.cpp
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index 0b41eeb..6dff669 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -457,6 +457,18 @@
const protobufs::DataDescriptor& dd2,
opt::IRContext* context);
+ // Computes various corollary facts from the data descriptor |dd| if members
+ // of its equivalence class participate in equation facts with OpConvert*
+ // opcodes. The descriptor should be registered in the equivalence relation.
+ void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+ opt::IRContext* context);
+
+ // Recurses into sub-components of the data descriptors, if they are
+ // composites, to record that their components are pairwise-synonymous.
+ void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+ const protobufs::DataDescriptor& dd2,
+ opt::IRContext* context);
+
// Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
// of equations that are known about them.
void MakeEquivalent(const protobufs::DataDescriptor& dd1,
@@ -588,9 +600,13 @@
// Now try to work out corollaries implied by the new equation and existing
// facts.
switch (opcode) {
+ case SpvOpConvertSToF:
+ case SpvOpConvertUToF:
+ ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
+ break;
case SpvOpIAdd: {
// Equation form: "a = b + c"
- for (auto equation : GetEquations(rhs_dds[0])) {
+ for (const auto& equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = (d - e) + c"
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
@@ -606,7 +622,7 @@
}
}
}
- for (auto equation : GetEquations(rhs_dds[1])) {
+ for (const auto& equation : GetEquations(rhs_dds[1])) {
if (equation.opcode == SpvOpISub) {
// Equation form: "a = b + (d - e)"
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
@@ -620,7 +636,7 @@
}
case SpvOpISub: {
// Equation form: "a = b - c"
- for (auto equation : GetEquations(rhs_dds[0])) {
+ for (const auto& equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = (d + e) - c"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
@@ -646,7 +662,7 @@
}
}
- for (auto equation : GetEquations(rhs_dds[1])) {
+ for (const auto& equation : GetEquations(rhs_dds[1])) {
if (equation.opcode == SpvOpIAdd) {
// Equation form: "a = b - (d + e)"
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
@@ -676,7 +692,7 @@
case SpvOpLogicalNot:
case SpvOpSNegate: {
// Equation form: "a = !b" or "a = -b"
- for (auto equation : GetEquations(rhs_dds[0])) {
+ for (const auto& equation : GetEquations(rhs_dds[0])) {
if (equation.opcode == opcode) {
// Equation form: "a = !!b" or "a = -(-b)"
// We can thus infer "a = b"
@@ -698,7 +714,69 @@
// Record that the data descriptors provided in the fact are equivalent.
MakeEquivalent(dd1, dd2);
- // We now check whether this is a synonym about composite objects. If it is,
+ // Compute various corollary facts.
+ ComputeConversionDataSynonymFacts(dd1, context);
+ ComputeCompositeDataSynonymFacts(dd1, dd2, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+ ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+ opt::IRContext* context) {
+ assert(synonymous_.Exists(dd) &&
+ "|dd| should've been registered in the equivalence relation");
+
+ const auto* representative = synonymous_.Find(&dd);
+ assert(representative &&
+ "Representative can't be null for a registered descriptor");
+
+ const auto* type =
+ context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices(
+ context, fuzzerutil::GetTypeId(context, representative->object()),
+ representative->index()));
+ assert(type && "Data descriptor has invalid type");
+
+ if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) ||
+ type->AsInteger()) {
+ // If there exist equation facts of the form |%a = opcode %representative|
+ // and |%b = opcode %representative| where |opcode| is either OpConvertSToF
+ // or OpConvertUToF, then |a| and |b| are synonymous.
+ std::vector<const protobufs::DataDescriptor*> convert_s_to_f_lhs;
+ std::vector<const protobufs::DataDescriptor*> convert_u_to_f_lhs;
+
+ for (const auto& fact : id_equations_) {
+ for (const auto& equation : fact.second) {
+ if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) {
+ if (equation.opcode == SpvOpConvertSToF) {
+ convert_s_to_f_lhs.push_back(fact.first);
+ } else if (equation.opcode == SpvOpConvertUToF) {
+ convert_u_to_f_lhs.push_back(fact.first);
+ }
+ }
+ }
+ }
+
+ for (const auto& synonyms :
+ {std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) {
+ for (const auto* synonym_a : synonyms) {
+ for (const auto* synonym_b : synonyms) {
+ if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) &&
+ DataDescriptorsAreWellFormedAndComparable(context, *synonym_a,
+ *synonym_b)) {
+ // |synonym_a| and |synonym_b| have compatible types - they are
+ // synonymous.
+ AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context);
+ }
+ }
+ }
+ }
+ }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+ ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+ const protobufs::DataDescriptor& dd2,
+ opt::IRContext* context) {
+ // Check whether this is a synonym about composite objects. If it is,
// we can recursively add synonym facts about their associated sub-components.
// Get the type of the object referred to by the first data descriptor in the
@@ -813,8 +891,10 @@
struct DataDescriptorPairEquals {
bool operator()(const DataDescriptorPair& first,
const DataDescriptorPair& second) const {
- return DataDescriptorEquals()(&first.first, &second.first) &&
- DataDescriptorEquals()(&first.second, &second.second);
+ return (DataDescriptorEquals()(&first.first, &second.first) &&
+ DataDescriptorEquals()(&first.second, &second.second)) ||
+ (DataDescriptorEquals()(&first.first, &second.second) &&
+ DataDescriptorEquals()(&first.second, &second.first));
}
};
@@ -1263,32 +1343,49 @@
//==============================
//==============================
-// Irrelevant pointee value facts
+// Irrelevant value facts
// The purpose of this class is to group the fields and data used to represent
-// facts about pointers whose pointee values are irrelevant.
-class FactManager::IrrelevantPointeeValueFacts {
+// facts about various irrelevant values in the module.
+class FactManager::IrrelevantValueFacts {
public:
// See method in FactManager which delegates to this method.
void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
// See method in FactManager which delegates to this method.
+ void AddFact(const protobufs::FactIdIsIrrelevant& fact);
+
+ // See method in FactManager which delegates to this method.
bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
+ // See method in FactManager which delegates to this method.
+ bool IdIsIrrelevant(uint32_t pointer_id) const;
+
private:
- std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+ std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+ std::unordered_set<uint32_t> irrelevant_ids_;
};
-void FactManager::IrrelevantPointeeValueFacts::AddFact(
+void FactManager::IrrelevantValueFacts::AddFact(
const protobufs::FactPointeeValueIsIrrelevant& fact) {
pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
}
-bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+void FactManager::IrrelevantValueFacts::AddFact(
+ const protobufs::FactIdIsIrrelevant& fact) {
+ irrelevant_ids_.insert(fact.result_id());
+}
+
+bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant(
uint32_t pointer_id) const {
return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
}
+bool FactManager::IrrelevantValueFacts::IdIsIrrelevant(
+ uint32_t pointer_id) const {
+ return irrelevant_ids_.count(pointer_id) != 0;
+}
+
// End of arbitrarily-valued variable facts
//==============================
@@ -1298,8 +1395,7 @@
MakeUnique<DataSynonymAndIdEquationFacts>()),
dead_block_facts_(MakeUnique<DeadBlockFacts>()),
livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
- irrelevant_pointee_value_facts_(
- MakeUnique<IrrelevantPointeeValueFacts>()) {}
+ irrelevant_value_facts_(MakeUnique<IrrelevantValueFacts>()) {}
FactManager::~FactManager() = default;
@@ -1340,6 +1436,8 @@
void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
const protobufs::DataDescriptor& data2,
opt::IRContext* context) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+ // assert that neither |data1| nor |data2| are irrelevant.
protobufs::FactDataSynonym fact;
*fact.mutable_data1() = data1;
*fact.mutable_data2() = data2;
@@ -1420,18 +1518,32 @@
}
bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
- return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+ return irrelevant_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+}
+
+bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
+ return irrelevant_value_facts_->IdIsIrrelevant(result_id);
}
void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
protobufs::FactPointeeValueIsIrrelevant fact;
fact.set_pointer_id(pointer_id);
- irrelevant_pointee_value_facts_->AddFact(fact);
+ irrelevant_value_facts_->AddFact(fact);
+}
+
+void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+ // assert that |result_id| is not a part of any DataSynonym fact.
+ protobufs::FactIdIsIrrelevant fact;
+ fact.set_result_id(result_id);
+ irrelevant_value_facts_->AddFact(fact);
}
void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
const std::vector<uint32_t>& rhs_id,
opt::IRContext* context) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+ // assert that elements of |rhs_id| and |lhs_id| are not irrelevant.
protobufs::FactIdEquation fact;
fact.set_lhs_id(lhs_id);
fact.set_opcode(opcode);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index f520e42..f83e2ff 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -68,6 +68,10 @@
// is irrelevant: it does not affect the observable behaviour of the module.
void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
+ // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
+ // the semantics of the module)
+ void AddFactIdIsIrrelevant(uint32_t result_id);
+
// Records the fact that |lhs_id| is defined by the equation:
//
// |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
@@ -181,13 +185,16 @@
//==============================
//==============================
- // Querying facts about pointers with irrelevant pointee values
+ // Querying facts about irrelevant values
// Returns true if and ony if the value of the pointee associated with
// |pointer_id| is irrelevant.
bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
- // End of irrelevant pointee value facts
+ // Returns true iff there exists a fact that the |result_id| is irrelevant.
+ bool IdIsIrrelevant(uint32_t result_id) const;
+
+ // End of irrelevant value facts
//==============================
private:
@@ -213,10 +220,10 @@
std::unique_ptr<LivesafeFunctionFacts>
livesafe_function_facts_; // Unique pointer to internal data.
- class IrrelevantPointeeValueFacts; // Opaque class for management of
- // facts about pointers whose pointee values do not matter.
- std::unique_ptr<IrrelevantPointeeValueFacts>
- irrelevant_pointee_value_facts_; // Unique pointer to internal data.
+ class IrrelevantValueFacts; // Opaque class for management of
+ // facts about various irrelevant values in the module.
+ std::unique_ptr<IrrelevantValueFacts>
+ irrelevant_value_facts_; // Unique pointer to internal data.
};
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 3343abc..f549590 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -18,36 +18,49 @@
#include <memory>
#include <sstream>
-#include "fuzzer_pass_adjust_memory_operands_masks.h"
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
#include "source/fuzz/fuzzer_pass_add_composite_types.h"
+#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
+#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+#include "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_pass_add_stores.h"
+#include "source/fuzz/fuzzer_pass_add_synonyms.h"
+#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
+#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
+#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
+#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
+#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/pseudo_random_generator.h"
@@ -195,6 +208,9 @@
MaybeAddPass<FuzzerPassAddCompositeTypes>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddCopyMemory>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddDeadBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
@@ -213,15 +229,27 @@
MaybeAddPass<FuzzerPassAddGlobalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLocalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddParameters>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddSynonyms>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassApplyIdSynonyms>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
@@ -234,6 +262,9 @@
MaybeAddPass<FuzzerPassDonateModules>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out, donor_suppliers);
+ MaybeAddPass<FuzzerPassInvertComparisonOperators>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassMergeBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
@@ -249,9 +280,24 @@
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassSplitBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
}
bool is_first = true;
@@ -289,6 +335,12 @@
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
+ &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassPermutePhiOperands>(
+ &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 1779709..3f1fe16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -27,19 +27,26 @@
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
+ {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
70};
@@ -51,19 +58,41 @@
const std::pair<uint32_t, uint32_t> kChanceOfCallingFunction = {1, 10};
const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
20, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
+ 50, 50};
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
{50, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
+ 10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
+ 20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
+ {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
+ {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
+const std::pair<uint32_t, uint32_t>
+ kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
+ {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
+ 30, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
+ 20, 40};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
+ {10, 70};
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
20, 90};
@@ -74,6 +103,11 @@
const uint32_t kDefaultMaxLoopControlPeelCount = 100;
const uint32_t kDefaultMaxLoopLimit = 20;
const uint32_t kDefaultMaxNewArraySizeLimit = 100;
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
+// think whether there is a better limit on the maximum number of parameters.
+const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
+const uint32_t kDefaultMaxNumberOfNewParameters = 15;
+const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5;
// Default functions for controlling how deep to go during recursive
// generation/transformation. Keep them in alphabetical order.
@@ -97,6 +131,10 @@
max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
max_loop_limit_(kDefaultMaxLoopLimit),
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
+ max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
+ max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
+ max_number_of_parameters_replaced_with_struct_(
+ kGetDefaultMaxNumberOfParametersReplacedWithStruct),
go_deeper_in_constant_obfuscation_(
kDefaultGoDeeperInConstantObfuscation) {
chance_of_adding_access_chain_ =
@@ -105,6 +143,8 @@
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
chance_of_adding_array_or_struct_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
+ chance_of_adding_copy_memory_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory);
chance_of_adding_dead_block_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock);
chance_of_adding_dead_break_ =
@@ -116,19 +156,28 @@
chance_of_adding_global_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+ chance_of_adding_image_sample_unused_components_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
chance_of_adding_local_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
chance_of_adding_matrix_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
chance_of_adding_no_contraction_decoration_ =
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+ chance_of_adding_parameters =
+ ChooseBetweenMinAndMax(kChanceOfAddingParameters);
+ chance_of_adding_relaxed_decoration_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
+ chance_of_adding_vector_shuffle_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
chance_of_adding_vector_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
chance_of_adjusting_branch_weights_ =
ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
chance_of_adjusting_function_control_ =
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
+ chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms);
chance_of_adjusting_loop_control_ =
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
chance_of_adjusting_memory_operands_mask_ =
@@ -139,6 +188,8 @@
ChooseBetweenMinAndMax(kChanceOfCallingFunction);
chance_of_choosing_struct_type_vs_array_type_ =
ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
+ chance_of_choosing_workgroup_storage_class_ =
+ ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass);
chance_of_constructing_composite_ =
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
@@ -146,6 +197,10 @@
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_going_deeper_when_making_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+ chance_of_interchanging_zero_like_constants_ =
+ ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
+ chance_of_inverting_comparison_operators_ =
+ ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
chance_of_making_donor_livesafe_ =
ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
@@ -157,9 +212,27 @@
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_permuting_parameters_ =
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
+ chance_of_permuting_phi_operands_ =
+ ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
+ chance_of_pushing_id_through_variable_ =
+ ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+ chance_of_replacing_copy_memory_with_load_store_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
+ chance_of_replacing_copyobject_with_store_load_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad);
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
+ chance_of_replacing_linear_algebra_instructions_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
+ chance_of_replacing_load_store_with_copy_memory_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
+ chance_of_replacing_parameters_with_globals_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
+ chance_of_replacing_parameters_with_struct_ =
+ ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+ chance_of_swapping_conditional_branch_operands_ =
+ ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
chance_of_toggling_access_chain_instruction_ =
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
}
@@ -168,6 +241,16 @@
uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; }
+std::vector<uint32_t> FuzzerContext::GetFreshIds(const uint32_t count) {
+ std::vector<uint32_t> fresh_ids(count);
+
+ for (uint32_t& fresh_id : fresh_ids) {
+ fresh_id = next_fresh_id_++;
+ }
+
+ return fresh_ids;
+}
+
bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); }
bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) {
@@ -182,5 +265,21 @@
random_generator_->RandomUint32(min_max.second - min_max.first + 1);
}
+protobufs::TransformationAddSynonym::SynonymType
+FuzzerContext::GetRandomSynonymType() {
+ // value_count method is guaranteed to return a value greater than 0.
+ auto result_index = ChooseBetweenMinAndMax(
+ {0, static_cast<uint32_t>(
+ protobufs::TransformationAddSynonym::SynonymType_descriptor()
+ ->value_count() -
+ 1)});
+ auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor()
+ ->value(result_index)
+ ->number();
+ assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) &&
+ "|result| is not a value of SynonymType");
+ return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index dd19d9a..3376c1e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -18,6 +18,7 @@
#include <functional>
#include <utility>
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/random_generator.h"
#include "source/opt/function.h"
@@ -100,6 +101,9 @@
// or to have been issued before.
uint32_t GetFreshId();
+ // Returns a vector of |count| fresh ids.
+ std::vector<uint32_t> GetFreshIds(const uint32_t count);
+
// Probabilities associated with applying various transformations.
// Keep them in alphabetical order.
uint32_t GetChanceOfAddingAccessChain() {
@@ -111,6 +115,9 @@
uint32_t GetChanceOfAddingArrayOrStructType() {
return chance_of_adding_array_or_struct_type_;
}
+ uint32_t GetChanceOfAddingCopyMemory() {
+ return chance_of_adding_copy_memory_;
+ }
uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
uint32_t GetChanceOfAddingDeadContinue() {
@@ -122,6 +129,9 @@
uint32_t GetChanceOfAddingGlobalVariable() {
return chance_of_adding_global_variable_;
}
+ uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
+ return chance_of_adding_image_sample_unused_components_;
+ }
uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
uint32_t GetChanceOfAddingLocalVariable() {
return chance_of_adding_local_variable_;
@@ -132,7 +142,15 @@
uint32_t GetChanceOfAddingNoContractionDecoration() {
return chance_of_adding_no_contraction_decoration_;
}
+ uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
+ uint32_t GetChanceOfAddingRelaxedDecoration() {
+ return chance_of_adding_relaxed_decoration_;
+ }
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
+ uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
+ uint32_t GetChanceOfAddingVectorShuffle() {
+ return chance_of_adding_vector_shuffle_;
+ }
uint32_t GetChanceOfAddingVectorType() {
return chance_of_adding_vector_type_;
}
@@ -155,6 +173,9 @@
uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
return chance_of_choosing_struct_type_vs_array_type_;
}
+ uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
+ return chance_of_choosing_workgroup_storage_class_;
+ }
uint32_t GetChanceOfConstructingComposite() {
return chance_of_constructing_composite_;
}
@@ -165,6 +186,12 @@
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
return chance_of_going_deeper_when_making_access_chain_;
}
+ uint32_t GetChanceOfInterchangingZeroLikeConstants() {
+ return chance_of_interchanging_zero_like_constants_;
+ }
+ uint32_t GetChanceOfInvertingComparisonOperators() {
+ return chance_of_inverting_comparison_operators_;
+ }
uint32_t ChanceOfMakingDonorLivesafe() {
return chance_of_making_donor_livesafe_;
}
@@ -179,10 +206,37 @@
uint32_t GetChanceOfPermutingParameters() {
return chance_of_permuting_parameters_;
}
+ uint32_t GetChanceOfPermutingPhiOperands() {
+ return chance_of_permuting_phi_operands_;
+ }
+ uint32_t GetChanceOfPushingIdThroughVariable() {
+ return chance_of_pushing_id_through_variable_;
+ }
+ uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+ return chance_of_replacing_copy_memory_with_load_store_;
+ }
+ uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
+ return chance_of_replacing_copyobject_with_store_load_;
+ }
uint32_t GetChanceOfReplacingIdWithSynonym() {
return chance_of_replacing_id_with_synonym_;
}
+ uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
+ return chance_of_replacing_linear_algebra_instructions_;
+ }
+ uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
+ return chance_of_replacing_load_store_with_copy_memory_;
+ }
+ uint32_t GetChanceOfReplacingParametersWithGlobals() {
+ return chance_of_replacing_parameters_with_globals_;
+ }
+ uint32_t GetChanceOfReplacingParametersWithStruct() {
+ return chance_of_replacing_parameters_with_struct_;
+ }
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+ uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+ return chance_of_swapping_conditional_branch_operands_;
+ }
uint32_t GetChanceOfTogglingAccessChainInstruction() {
return chance_of_toggling_access_chain_instruction_;
}
@@ -192,17 +246,11 @@
uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
return max_equivalence_class_size_for_data_synonym_fact_closure_;
}
- uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
- return random_generator_->RandomUint32(composite_size_bound);
+ uint32_t GetMaximumNumberOfFunctionParameters() {
+ return max_number_of_function_parameters_;
}
- uint32_t GetRandomLoopControlPartialCount() {
- return random_generator_->RandomUint32(max_loop_control_partial_count_);
- }
- uint32_t GetRandomLoopControlPeelCount() {
- return random_generator_->RandomUint32(max_loop_control_peel_count_);
- }
- uint32_t GetRandomLoopLimit() {
- return random_generator_->RandomUint32(max_loop_limit_);
+ uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
+ return max_number_of_parameters_replaced_with_struct_;
}
std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
@@ -216,10 +264,51 @@
return branch_weights;
}
+ std::vector<uint32_t> GetRandomComponentsForVectorShuffle(
+ uint32_t max_component_index) {
+ // Component count must be in range [2, 4].
+ std::vector<uint32_t> components(random_generator_->RandomUint32(2) + 2);
+
+ for (uint32_t& component : components) {
+ component = random_generator_->RandomUint32(max_component_index);
+ }
+
+ return components;
+ }
+ uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
+ return random_generator_->RandomUint32(composite_size_bound);
+ }
+ uint32_t GetRandomLoopControlPartialCount() {
+ return random_generator_->RandomUint32(max_loop_control_partial_count_);
+ }
+ uint32_t GetRandomLoopControlPeelCount() {
+ return random_generator_->RandomUint32(max_loop_control_peel_count_);
+ }
+ uint32_t GetRandomLoopLimit() {
+ return random_generator_->RandomUint32(max_loop_limit_);
+ }
+ uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
+ assert(num_of_params < GetMaximumNumberOfFunctionParameters());
+ return ChooseBetweenMinAndMax(
+ {1, std::min(max_number_of_new_parameters_,
+ GetMaximumNumberOfFunctionParameters() - num_of_params)});
+ }
+ uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) {
+ assert(num_params != 0 && "A function must have parameters to replace");
+ return ChooseBetweenMinAndMax(
+ {1, std::min(num_params,
+ GetMaximumNumberOfParametersReplacedWithStruct())});
+ }
uint32_t GetRandomSizeForNewArray() {
// Ensure that the array size is non-zero.
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
}
+ protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType();
+ uint32_t GetRandomUnusedComponentCountForImageSample(
+ uint32_t max_unused_component_count) {
+ // Ensure that the number of unused components is non-zero.
+ return random_generator_->RandomUint32(max_unused_component_count) + 1;
+ }
bool GoDeeperInConstantObfuscation(uint32_t depth) {
return go_deeper_in_constant_obfuscation_(depth, random_generator_);
}
@@ -235,16 +324,22 @@
uint32_t chance_of_adding_access_chain_;
uint32_t chance_of_adding_another_struct_field_;
uint32_t chance_of_adding_array_or_struct_type_;
+ uint32_t chance_of_adding_copy_memory_;
uint32_t chance_of_adding_dead_block_;
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
uint32_t chance_of_adding_equation_instruction_;
uint32_t chance_of_adding_global_variable_;
+ uint32_t chance_of_adding_image_sample_unused_components_;
uint32_t chance_of_adding_load_;
uint32_t chance_of_adding_local_variable_;
uint32_t chance_of_adding_matrix_type_;
uint32_t chance_of_adding_no_contraction_decoration_;
+ uint32_t chance_of_adding_parameters;
+ uint32_t chance_of_adding_relaxed_decoration_;
uint32_t chance_of_adding_store_;
+ uint32_t chance_of_adding_synonyms_;
+ uint32_t chance_of_adding_vector_shuffle_;
uint32_t chance_of_adding_vector_type_;
uint32_t chance_of_adjusting_branch_weights_;
uint32_t chance_of_adjusting_function_control_;
@@ -253,18 +348,30 @@
uint32_t chance_of_adjusting_selection_control_;
uint32_t chance_of_calling_function_;
uint32_t chance_of_choosing_struct_type_vs_array_type_;
+ uint32_t chance_of_choosing_workgroup_storage_class_;
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_going_deeper_when_making_access_chain_;
+ uint32_t chance_of_interchanging_zero_like_constants_;
+ uint32_t chance_of_inverting_comparison_operators_;
uint32_t chance_of_making_donor_livesafe_;
uint32_t chance_of_merging_blocks_;
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
uint32_t chance_of_permuting_parameters_;
+ uint32_t chance_of_permuting_phi_operands_;
+ uint32_t chance_of_pushing_id_through_variable_;
+ uint32_t chance_of_replacing_copy_memory_with_load_store_;
+ uint32_t chance_of_replacing_copyobject_with_store_load_;
uint32_t chance_of_replacing_id_with_synonym_;
+ uint32_t chance_of_replacing_linear_algebra_instructions_;
+ uint32_t chance_of_replacing_load_store_with_copy_memory_;
+ uint32_t chance_of_replacing_parameters_with_globals_;
+ uint32_t chance_of_replacing_parameters_with_struct_;
uint32_t chance_of_splitting_block_;
+ uint32_t chance_of_swapping_conditional_branch_operands_;
uint32_t chance_of_toggling_access_chain_instruction_;
// Limits associated with various quantities for which random values are
@@ -275,6 +382,9 @@
uint32_t max_loop_control_peel_count_;
uint32_t max_loop_limit_;
uint32_t max_new_array_size_limit_;
+ uint32_t max_number_of_function_parameters_;
+ uint32_t max_number_of_new_parameters_;
+ uint32_t max_number_of_parameters_replaced_with_struct_;
// Functions to determine with what probability to go deeper when generating
// or mutating constructs recursively.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
index cd94e4e..ebd88d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -20,6 +20,7 @@
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_null.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_type_boolean.h"
@@ -28,6 +29,7 @@
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_matrix.h"
#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_add_type_struct.h"
#include "source/fuzz/transformation_add_type_vector.h"
namespace spvtools {
@@ -151,9 +153,7 @@
}
uint32_t FuzzerPass::FindOrCreateBoolType() {
- opt::analysis::Bool bool_type;
- auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
- if (existing_id) {
+ if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
@@ -161,25 +161,25 @@
return result;
}
-uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
- opt::analysis::Integer int_type(32, is_signed);
+uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
+ opt::analysis::Integer int_type(width, is_signed);
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed));
+ ApplyTransformation(TransformationAddTypeInt(result, width, is_signed));
return result;
}
-uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
- opt::analysis::Float float_type(32);
+uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
+ opt::analysis::Float float_type(width);
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(TransformationAddTypeFloat(result, 32));
+ ApplyTransformation(TransformationAddTypeFloat(result, width));
return result;
}
@@ -228,7 +228,7 @@
assert(row_count >= 2 && row_count <= 4 &&
"Precondition: row count must be in range [2, 4].");
uint32_t column_type_id =
- FindOrCreateVectorType(FindOrCreate32BitFloatType(), row_count);
+ FindOrCreateVectorType(FindOrCreateFloatType(32), row_count);
opt::analysis::Type* column_type =
GetIRContext()->get_type_mgr()->GetType(column_type_id);
opt::analysis::Matrix matrix_type(column_type, column_count);
@@ -242,6 +242,17 @@
return result;
}
+uint32_t FuzzerPass::FindOrCreateStructType(
+ const std::vector<uint32_t>& component_type_ids) {
+ if (auto existing_id =
+ fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) {
+ return existing_id;
+ }
+ auto new_id = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids));
+ return new_id;
+}
+
uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
SpvStorageClass storage_class) {
// We do not use the type manager here, due to problems related to isomorphic
@@ -257,70 +268,59 @@
return result;
}
-uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
- bool is_signed, SpvStorageClass storage_class) {
- return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed),
+uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
+ uint32_t width, bool is_signed, SpvStorageClass storage_class) {
+ return FindOrCreatePointerType(FindOrCreateIntegerType(width, is_signed),
storage_class);
}
-uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
- bool is_signed) {
- auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
- opt::analysis::IntConstant int_constant(
- GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(),
- {word});
- auto existing_constant =
- GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
- if (existing_constant) {
- return GetIRContext()
- ->get_constant_mgr()
- ->GetDefiningInstruction(existing_constant)
- ->result_id();
+uint32_t FuzzerPass::FindOrCreateIntegerConstant(
+ const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+ bool is_irrelevant) {
+ auto int_type_id = FindOrCreateIntegerType(width, is_signed);
+ if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+ GetIRContext(), *GetTransformationContext(), words, int_type_id,
+ is_irrelevant)) {
+ return constant_id;
}
auto result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(
- TransformationAddConstantScalar(result, uint32_type_id, {word}));
+ ApplyTransformation(TransformationAddConstantScalar(result, int_type_id,
+ words, is_irrelevant));
return result;
}
-uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
- auto float_type_id = FindOrCreate32BitFloatType();
+uint32_t FuzzerPass::FindOrCreateFloatConstant(
+ const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
+ auto float_type_id = FindOrCreateFloatType(width);
opt::analysis::FloatConstant float_constant(
- GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(),
- {word});
- auto existing_constant =
- GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
- if (existing_constant) {
- return GetIRContext()
- ->get_constant_mgr()
- ->GetDefiningInstruction(existing_constant)
- ->result_id();
+ GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), words);
+ if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+ GetIRContext(), *GetTransformationContext(), words, float_type_id,
+ is_irrelevant)) {
+ return constant_id;
}
auto result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(
- TransformationAddConstantScalar(result, float_type_id, {word}));
+ ApplyTransformation(TransformationAddConstantScalar(result, float_type_id,
+ words, is_irrelevant));
return result;
}
-uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
+uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) {
auto bool_type_id = FindOrCreateBoolType();
- opt::analysis::BoolConstant bool_constant(
- GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
- auto existing_constant =
- GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
- if (existing_constant) {
- return GetIRContext()
- ->get_constant_mgr()
- ->GetDefiningInstruction(existing_constant)
- ->result_id();
+ if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+ GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u},
+ bool_type_id, is_irrelevant)) {
+ return constant_id;
}
auto result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(TransformationAddConstantBoolean(result, value));
+ ApplyTransformation(
+ TransformationAddConstantBoolean(result, value, is_irrelevant));
return result;
}
uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
- uint32_t type_id) {
+ uint32_t type_id,
+ bool is_irrelevant) {
assert(type_id && "Constant's type id can't be 0.");
const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
@@ -328,18 +328,12 @@
if (type->AsBool()) {
assert(words.size() == 1);
- return FindOrCreateBoolConstant(words[0]);
+ return FindOrCreateBoolConstant(words[0], is_irrelevant);
} else if (const auto* integer = type->AsInteger()) {
- assert(integer->width() == 32 && words.size() == 1 &&
- "Integer must have 32-bit width");
- return FindOrCreate32BitIntegerConstant(words[0], integer->IsSigned());
+ return FindOrCreateIntegerConstant(words, integer->width(),
+ integer->IsSigned(), is_irrelevant);
} else if (const auto* floating = type->AsFloat()) {
- // Assertions are not evaluated in release builds so |floating|
- // variable will be unused.
- (void)floating;
- assert(floating->width() == 32 && words.size() == 1 &&
- "Floating point number must have 32-bit width");
- return FindOrCreate32BitFloatConstant(words[0]);
+ return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant);
}
// This assertion will fail in debug build but not in release build
@@ -348,6 +342,20 @@
return 0;
}
+uint32_t FuzzerPass::FindOrCreateCompositeConstant(
+ const std::vector<uint32_t>& component_ids, uint32_t type_id,
+ bool is_irrelevant) {
+ if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant(
+ GetIRContext(), *GetTransformationContext(), component_ids, type_id,
+ is_irrelevant)) {
+ return existing_constant;
+ }
+ uint32_t result = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationAddConstantComposite(
+ result, type_id, component_ids, is_irrelevant));
+ return result;
+}
+
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
for (auto& inst : GetIRContext()->types_values()) {
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
@@ -359,6 +367,27 @@
return result;
}
+uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
+ // Find existing declaration
+ opt::analysis::NullConstant null_constant(
+ GetIRContext()->get_type_mgr()->GetType(type_id));
+ auto existing_constant =
+ GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
+
+ // Return if found
+ if (existing_constant) {
+ return GetIRContext()
+ ->get_constant_mgr()
+ ->GetDefiningInstruction(existing_constant)
+ ->result_id();
+ }
+
+ // Create new if not found
+ auto result = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationAddConstantNull(result, type_id));
+ return result;
+}
+
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
FuzzerPass::GetAvailableBasicTypesAndPointers(
SpvStorageClass storage_class) const {
@@ -429,43 +458,55 @@
}
uint32_t FuzzerPass::FindOrCreateZeroConstant(
- uint32_t scalar_or_composite_type_id) {
+ uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
auto type_instruction =
GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
assert(type_instruction && "The type instruction must exist.");
switch (type_instruction->opcode()) {
case SpvOpTypeBool:
- return FindOrCreateBoolConstant(false);
- case SpvOpTypeFloat:
- return FindOrCreate32BitFloatConstant(0);
- case SpvOpTypeInt:
- return FindOrCreate32BitIntegerConstant(
- 0, type_instruction->GetSingleWordInOperand(1) != 0);
+ return FindOrCreateBoolConstant(false, is_irrelevant);
+ case SpvOpTypeFloat: {
+ auto width = type_instruction->GetSingleWordInOperand(0);
+ auto num_words = (width + 32 - 1) / 32;
+ return FindOrCreateFloatConstant(std::vector<uint32_t>(num_words, 0),
+ width, is_irrelevant);
+ }
+ case SpvOpTypeInt: {
+ auto width = type_instruction->GetSingleWordInOperand(0);
+ auto num_words = (width + 32 - 1) / 32;
+ return FindOrCreateIntegerConstant(
+ std::vector<uint32_t>(num_words, 0), width,
+ type_instruction->GetSingleWordInOperand(1), is_irrelevant);
+ }
case SpvOpTypeArray: {
- return GetZeroConstantForHomogeneousComposite(
- *type_instruction, type_instruction->GetSingleWordInOperand(0),
- fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
+ auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+ auto num_components =
+ fuzzerutil::GetArraySize(*type_instruction, GetIRContext());
+ return FindOrCreateCompositeConstant(
+ std::vector<uint32_t>(
+ num_components,
+ FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+ scalar_or_composite_type_id, is_irrelevant);
}
case SpvOpTypeMatrix:
case SpvOpTypeVector: {
- return GetZeroConstantForHomogeneousComposite(
- *type_instruction, type_instruction->GetSingleWordInOperand(0),
- type_instruction->GetSingleWordInOperand(1));
+ auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+ auto num_components = type_instruction->GetSingleWordInOperand(1);
+ return FindOrCreateCompositeConstant(
+ std::vector<uint32_t>(
+ num_components,
+ FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+ scalar_or_composite_type_id, is_irrelevant);
}
case SpvOpTypeStruct: {
- std::vector<const opt::analysis::Constant*> field_zero_constants;
std::vector<uint32_t> field_zero_ids;
for (uint32_t index = 0; index < type_instruction->NumInOperands();
index++) {
- uint32_t field_constant_id = FindOrCreateZeroConstant(
- type_instruction->GetSingleWordInOperand(index));
- field_zero_ids.push_back(field_constant_id);
- field_zero_constants.push_back(
- GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
- field_constant_id));
+ field_zero_ids.push_back(FindOrCreateZeroConstant(
+ type_instruction->GetSingleWordInOperand(index), is_irrelevant));
}
return FindOrCreateCompositeConstant(
- *type_instruction, field_zero_constants, field_zero_ids);
+ field_zero_ids, scalar_or_composite_type_id, is_irrelevant);
}
default:
assert(false && "Unknown type.");
@@ -473,62 +514,5 @@
}
}
-uint32_t FuzzerPass::FindOrCreateCompositeConstant(
- const opt::Instruction& composite_type_instruction,
- const std::vector<const opt::analysis::Constant*>& constants,
- const std::vector<uint32_t>& constant_ids) {
- assert(constants.size() == constant_ids.size() &&
- "Precondition: |constants| and |constant_ids| must be in "
- "correspondence.");
-
- opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
- composite_type_instruction.result_id());
- std::unique_ptr<opt::analysis::Constant> composite_constant;
- if (composite_type->AsArray()) {
- composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
- composite_type->AsArray(), constants);
- } else if (composite_type->AsMatrix()) {
- composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
- composite_type->AsMatrix(), constants);
- } else if (composite_type->AsStruct()) {
- composite_constant = MakeUnique<opt::analysis::StructConstant>(
- composite_type->AsStruct(), constants);
- } else if (composite_type->AsVector()) {
- composite_constant = MakeUnique<opt::analysis::VectorConstant>(
- composite_type->AsVector(), constants);
- } else {
- assert(false &&
- "Precondition: |composite_type| must declare a composite type.");
- return 0;
- }
-
- uint32_t existing_constant =
- GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
- composite_constant.get(), composite_type_instruction.result_id());
- if (existing_constant) {
- return existing_constant;
- }
- uint32_t result = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(TransformationAddConstantComposite(
- result, composite_type_instruction.result_id(), constant_ids));
- return result;
-}
-
-uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
- const opt::Instruction& composite_type_instruction,
- uint32_t component_type_id, uint32_t num_components) {
- std::vector<const opt::analysis::Constant*> zero_constants;
- std::vector<uint32_t> zero_ids;
- uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
- const opt::analysis::Constant* registered_zero_component =
- GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
- for (uint32_t i = 0; i < num_components; i++) {
- zero_constants.push_back(registered_zero_component);
- zero_ids.push_back(zero_component);
- }
- return FindOrCreateCompositeConstant(composite_type_instruction,
- zero_constants, zero_ids);
-}
-
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
index 800b888..4731b74 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -103,18 +103,33 @@
*GetTransformations()->add_transformation() = transformation.ToMessage();
}
+ // A generic helper for applying a transformation only if it is applicable.
+ // If it is applicable, the transformation is applied and then added to the
+ // sequence of applied transformations and the function returns true.
+ // Otherwise, the function returns false.
+ bool MaybeApplyTransformation(const Transformation& transformation) {
+ if (transformation.IsApplicable(GetIRContext(),
+ *GetTransformationContext())) {
+ transformation.Apply(GetIRContext(), GetTransformationContext());
+ *GetTransformations()->add_transformation() = transformation.ToMessage();
+ return true;
+ }
+ return false;
+ }
+
// Returns the id of an OpTypeBool instruction. If such an instruction does
// not exist, a transformation is applied to add it.
uint32_t FindOrCreateBoolType();
- // Returns the id of an OpTypeInt instruction, with width 32 and signedness
- // specified by |is_signed|. If such an instruction does not exist, a
- // transformation is applied to add it.
- uint32_t FindOrCreate32BitIntegerType(bool is_signed);
+ // Returns the id of an OpTypeInt instruction, with width and signedness
+ // specified by |width| and |is_signed|, respectively. If such an instruction
+ // does not exist, a transformation is applied to add it.
+ uint32_t FindOrCreateIntegerType(uint32_t width, bool is_signed);
- // Returns the id of an OpTypeFloat instruction, with width 32. If such an
- // instruction does not exist, a transformation is applied to add it.
- uint32_t FindOrCreate32BitFloatType();
+ // Returns the id of an OpTypeFloat instruction, with width specified by
+ // |width|. If such an instruction does not exist, a transformation is
+ // applied to add it.
+ uint32_t FindOrCreateFloatType(uint32_t width);
// Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
// instruction. If such an instruction doesn't exist, a transformation
@@ -135,48 +150,83 @@
// type itself do not exist, transformations are applied to add them.
uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
+ // Returns the id of an OpTypeStruct instruction with |component_type_ids| as
+ // type ids for struct's components. If no such a struct type exists,
+ // transformations are applied to add it. |component_type_ids| may not contain
+ // a result id of an OpTypeFunction.
+ uint32_t FindOrCreateStructType(
+ const std::vector<uint32_t>& component_type_ids);
+
// Returns the id of a pointer type with base type |base_type_id| (which must
// already exist) and storage class |storage_class|. A transformation is
// applied to add the pointer if it does not already exist.
uint32_t FindOrCreatePointerType(uint32_t base_type_id,
SpvStorageClass storage_class);
- // Returns the id of an OpTypePointer instruction, with a 32-bit integer base
- // type of signedness specified by |is_signed|. If the pointer type or
- // required integer base type do not exist, transformations are applied to add
- // them.
- uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed,
- SpvStorageClass storage_class);
+ // Returns the id of an OpTypePointer instruction, with a integer base
+ // type of width and signedness specified by |width| and |is_signed|,
+ // respectively. If the pointer type or required integer base type do not
+ // exist, transformations are applied to add them.
+ uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed,
+ SpvStorageClass storage_class);
- // Returns the id of an OpConstant instruction, with 32-bit integer type of
- // signedness specified by |is_signed|, with |word| as its value. If either
- // the required integer type or the constant do not exist, transformations are
- // applied to add them.
- uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
+ // Returns the id of an OpConstant instruction, with a integer type of
+ // width and signedness specified by |width| and |is_signed|, respectively,
+ // with |words| as its value. If either the required integer type or the
+ // constant do not exist, transformations are applied to add them.
+ // The returned id either participates in IdIsIrrelevant fact or not,
+ // depending on the |is_irrelevant| parameter.
+ uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
+ uint32_t width, bool is_signed,
+ bool is_irrelevant);
- // Returns the id of an OpConstant instruction, with 32-bit floating-point
- // type, with |word| as its value. If either the required floating-point type
- // or the constant do not exist, transformations are applied to add them.
- uint32_t FindOrCreate32BitFloatConstant(uint32_t word);
+ // Returns the id of an OpConstant instruction, with a floating-point
+ // type of width specified by |width|, with |words| as its value. If either
+ // the required floating-point type or the constant do not exist,
+ // transformations are applied to add them. The returned id either
+ // participates in IdIsIrrelevant fact or not, depending on the
+ // |is_irrelevant| parameter.
+ uint32_t FindOrCreateFloatConstant(const std::vector<uint32_t>& words,
+ uint32_t width, bool is_irrelevant);
// Returns the id of an OpConstantTrue or OpConstantFalse instruction,
// according to |value|. If either the required instruction or the bool
// type do not exist, transformations are applied to add them.
- uint32_t FindOrCreateBoolConstant(bool value);
+ // The returned id either participates in IdIsIrrelevant fact or not,
+ // depending on the |is_irrelevant| parameter.
+ uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant);
// Returns the id of an OpConstant instruction of type with |type_id|
// that consists of |words|. If that instruction doesn't exist,
// transformations are applied to add it. |type_id| must be a valid
// result id of either scalar or boolean OpType* instruction that exists
- // in the module.
+ // in the module. The returned id either participates in IdIsIrrelevant fact
+ // or not, depending on the |is_irrelevant| parameter.
uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
- uint32_t type_id);
+ uint32_t type_id, bool is_irrelevant);
+
+ // Returns the id of an OpConstantComposite instruction of type with |type_id|
+ // that consists of |component_ids|. If that instruction doesn't exist,
+ // transformations are applied to add it. |type_id| must be a valid
+ // result id of an OpType* instruction that represents a composite type
+ // (i.e. a vector, matrix, struct or array).
+ // The returned id either participates in IdIsIrrelevant fact or not,
+ // depending on the |is_irrelevant| parameter.
+ uint32_t FindOrCreateCompositeConstant(
+ const std::vector<uint32_t>& component_ids, uint32_t type_id,
+ bool is_irrelevant);
// Returns the result id of an instruction of the form:
// %id = OpUndef %|type_id|
// If no such instruction exists, a transformation is applied to add it.
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
+ // Returns the id of an OpNullConstant instruction of type |type_id|. If
+ // that instruction doesn't exist, it is added through a transformation.
+ // |type_id| must be a valid result id of an OpType* instruction that exists
+ // in the module.
+ uint32_t FindOrCreateNullConstant(uint32_t type_id);
+
// Define a *basic type* to be an integer, boolean or floating-point type,
// or a matrix, vector, struct or fixed-size array built from basic types. In
// particular, a basic type cannot contain an opaque type (such as an image),
@@ -197,7 +247,8 @@
// some scalar or composite type, returns the result id of an instruction
// defining a constant of the given type that is zero or false at everywhere.
// If such an instruction does not yet exist, transformations are applied to
- // add it.
+ // add it. The returned id either participates in IdIsIrrelevant fact or not,
+ // depending on the |is_irrelevant| parameter.
//
// Examples:
// --------------+-------------------------------
@@ -219,31 +270,10 @@
// uint2 u; |
// } |
// --------------+-------------------------------
- uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
+ uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
+ bool is_irrelevant);
private:
- // Array, matrix and vector are *homogeneous* composite types in the sense
- // that every component of one of these types has the same type. Given a
- // homogeneous composite type instruction, |composite_type_instruction|,
- // returns the id of a composite constant instruction for which every element
- // is zero/false. If such an instruction does not yet exist, transformations
- // are applied to add it.
- uint32_t GetZeroConstantForHomogeneousComposite(
- const opt::Instruction& composite_type_instruction,
- uint32_t component_type_id, uint32_t num_components);
-
- // Helper to find an existing composite constant instruction of the given
- // composite type with the given constant components, or to apply
- // transformations to create such an instruction if it does not yet exist.
- // Parameter |composite_type_instruction| must be a composite type
- // instruction. The parameters |constants| and |constant_ids| must have the
- // same size, and it must be the case that for each i, |constant_ids[i]| is
- // the result id of an instruction that defines |constants[i]|.
- uint32_t FindOrCreateCompositeConstant(
- const opt::Instruction& composite_type_instruction,
- const std::vector<const opt::analysis::Constant*>& constants,
- const std::vector<uint32_t>& constant_ids);
-
opt::IRContext* ir_context_;
TransformationContext* transformation_context_;
FuzzerContext* fuzzer_context_;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
index b9c1eed..11155f2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -92,6 +92,11 @@
relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
relevant_pointer_instructions)];
std::vector<uint32_t> index_ids;
+
+ // Each index accessing a non-struct composite will be clamped, thus
+ // needing a pair of fresh ids
+ std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
+
auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
chosen_pointer->type_id());
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
@@ -132,20 +137,37 @@
break;
}
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
- // could allow non-constant indices when looking up non-structs,
- // using clamping to ensure they are in-bounds.
uint32_t index_value =
GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
- index_ids.push_back(FindOrCreate32BitIntegerConstant(
- index_value, GetFuzzerContext()->ChooseEven()));
+
switch (subobject_type->opcode()) {
case SpvOpTypeArray:
case SpvOpTypeMatrix:
- case SpvOpTypeVector:
+ case SpvOpTypeVector: {
+ // The index will be clamped
+
+ bool is_signed = GetFuzzerContext()->ChooseEven();
+
+ // Make the constant ready for clamping. We need:
+ // - an OpTypeBool to be present in the module
+ // - an OpConstant with the same type as the index and value
+ // the maximum value for an index
+ // - a new pair of fresh ids for the clamping instructions
+ FindOrCreateBoolType();
+ FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
+ std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
+ GetFuzzerContext()->GetFreshId(),
+ GetFuzzerContext()->GetFreshId()};
+ fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
+
+ index_ids.push_back(FindOrCreateIntegerConstant(
+ {index_value}, 32, is_signed, false));
subobject_type_id = subobject_type->GetSingleWordInOperand(0);
- break;
+
+ } break;
case SpvOpTypeStruct:
+ index_ids.push_back(FindOrCreateIntegerConstant(
+ {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
subobject_type_id =
subobject_type->GetSingleWordInOperand(index_value);
break;
@@ -162,7 +184,7 @@
// Apply the transformation to add an access chain.
ApplyTransformation(TransformationAccessChain(
GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
- index_ids, instruction_descriptor));
+ index_ids, instruction_descriptor, fresh_ids_for_clamping));
});
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
index 9b0dda8..653d784 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
@@ -53,13 +53,13 @@
return FindOrCreateBoolType();
};
std::function<uint32_t()> float_type_supplier = [this]() -> uint32_t {
- return FindOrCreate32BitFloatType();
+ return FindOrCreateFloatType(32);
};
std::function<uint32_t()> int_type_supplier = [this]() -> uint32_t {
- return FindOrCreate32BitIntegerType(true);
+ return FindOrCreateIntegerType(32, true);
};
std::function<uint32_t()> uint_type_supplier = [this]() -> uint32_t {
- return FindOrCreate32BitIntegerType(false);
+ return FindOrCreateIntegerType(32, false);
};
// Consider each of the base types with which we can make vectors.
@@ -96,8 +96,8 @@
void FuzzerPassAddCompositeTypes::AddNewArrayType() {
ApplyTransformation(TransformationAddTypeArray(
GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
- FindOrCreate32BitIntegerConstant(
- GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+ FindOrCreateIntegerConstant(
+ {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false)));
}
void FuzzerPassAddCompositeTypes::AddNewStructType() {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp
new file mode 100644
index 0000000..d98619c
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_copy_memory.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_copy_memory.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default;
+
+void FuzzerPassAddCopyMemory::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ // Check that we can insert an OpCopyMemory before this instruction.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory,
+ inst_it)) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingCopyMemory())) {
+ return;
+ }
+
+ // Get all instructions available before |inst_it| according to the
+ // domination rules.
+ auto instructions = FindAvailableInstructions(
+ function, block, inst_it,
+ TransformationAddCopyMemory::IsInstructionSupported);
+
+ if (instructions.empty()) {
+ return;
+ }
+
+ const auto* inst =
+ instructions[GetFuzzerContext()->RandomIndex(instructions)];
+
+ // Decide whether to create global or local variable.
+ auto storage_class = GetFuzzerContext()->ChooseEven()
+ ? SpvStorageClassPrivate
+ : SpvStorageClassFunction;
+
+ auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ GetIRContext(), inst->type_id());
+
+ // Create a pointer type with |storage_class| if needed.
+ FindOrCreatePointerType(pointee_type_id, storage_class);
+
+ ApplyTransformation(TransformationAddCopyMemory(
+ instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+ inst->result_id(), storage_class,
+ FindOrCreateZeroConstant(pointee_type_id, false)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h
new file mode 100644
index 0000000..321e4a1
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_copy_memory.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides whether to add OpCopyMemory before some instruction in the
+// module.
+class FuzzerPassAddCopyMemory : public FuzzerPass {
+ public:
+ FuzzerPassAddCopyMemory(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddCopyMemory() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
index 30a4145..84ed1fb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
@@ -45,7 +45,7 @@
// Make sure the module contains a boolean constant equal to
// |condition_value|.
bool condition_value = GetFuzzerContext()->ChooseEven();
- FindOrCreateBoolConstant(condition_value);
+ FindOrCreateBoolConstant(condition_value, false);
// We speculatively create a transformation, and then apply it (below) if
// it turns out to be applicable. This avoids duplicating the logic for
@@ -59,11 +59,7 @@
}
// Apply all those transformations that are in fact applicable.
for (auto& transformation : candidate_transformations) {
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
- }
+ MaybeApplyTransformation(transformation);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index f3900aa..cf4ecee 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -68,19 +68,16 @@
merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
// Add an additional operand for OpPhi instruction.
//
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
- // If we have a way to communicate to the fact manager
- // that a specific id use is irrelevant and could be replaced with
- // something else, we should add such a fact about the zero
- // provided as an OpPhi operand
- phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+ // We mark the constant as irrelevant so that we can replace it with
+ // a more interesting value later.
+ phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
});
}
// Make sure the module has a required boolean constant to be used in
// OpBranchConditional instruction.
auto break_condition = GetFuzzerContext()->ChooseEven();
- FindOrCreateBoolConstant(break_condition);
+ FindOrCreateBoolConstant(break_condition, false);
auto candidate_transformation = TransformationAddDeadBreak(
block.id(), merge_block->id(), break_condition, std::move(phi_ids));
@@ -114,12 +111,9 @@
candidate_transformations.erase(candidate_transformations.begin() + index);
// Probabilistically decide whether to try to apply it vs. ignore it, in the
// case that it is applicable.
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext()) &&
- GetFuzzerContext()->ChoosePercentage(
+ if (GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
+ MaybeApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 56a7fd1..61a7c6d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -59,19 +59,16 @@
continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
// Add an additional operand for OpPhi instruction.
//
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
- // If we have a way to communicate to the fact manager
- // that a specific id use is irrelevant and could be replaced with
- // something else, we should add such a fact about the zero
- // provided as an OpPhi operand
- phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+ // We mark the constant as irrelevant so that we can replace it with a
+ // more interesting value later.
+ phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
});
}
// Make sure the module contains a boolean constant equal to
// |condition_value|.
bool condition_value = GetFuzzerContext()->ChooseEven();
- FindOrCreateBoolConstant(condition_value);
+ FindOrCreateBoolConstant(condition_value, false);
// Make a transformation to add a dead continue from this node; if the
// node turns out to be inappropriate (e.g. by not being in a loop) the
@@ -80,14 +77,9 @@
block.id(), condition_value, std::move(phi_ids));
// Probabilistically decide whether to apply the transformation in the
// case that it is applicable.
- if (candidate_transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext()) &&
- GetFuzzerContext()->ChoosePercentage(
+ if (GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
- candidate_transformation.Apply(GetIRContext(),
- GetTransformationContext());
- *GetTransformations()->add_transformation() =
- candidate_transformation.ToMessage();
+ MaybeApplyTransformation(candidate_transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
index 49c4a8a..dc811e6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -57,19 +57,116 @@
std::vector<opt::Instruction*> available_instructions =
FindAvailableInstructions(
function, block, inst_it,
- [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+ [this](opt::IRContext*, opt::Instruction* instruction) -> bool {
return instruction->result_id() && instruction->type_id() &&
- instruction->opcode() != SpvOpUndef;
+ instruction->opcode() != SpvOpUndef &&
+ !GetTransformationContext()
+ ->GetFactManager()
+ ->IdIsIrrelevant(instruction->result_id());
});
// Try the opcodes for which we know how to make ids at random until
// something works.
- std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
- SpvOpLogicalNot, SpvOpSNegate};
+ std::vector<SpvOp> candidate_opcodes = {
+ SpvOpIAdd, SpvOpISub, SpvOpLogicalNot, SpvOpSNegate,
+ SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
do {
auto opcode =
GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
switch (opcode) {
+ case SpvOpConvertSToF:
+ case SpvOpConvertUToF: {
+ auto candidate_instructions =
+ GetIntegerInstructions(available_instructions);
+
+ if (candidate_instructions.empty()) {
+ break;
+ }
+
+ const auto* operand =
+ candidate_instructions[GetFuzzerContext()->RandomIndex(
+ candidate_instructions)];
+
+ const auto* type =
+ GetIRContext()->get_type_mgr()->GetType(operand->type_id());
+ assert(type && "Operand has invalid type");
+
+ // Make sure a result type exists in the module.
+ if (const auto* vector = type->AsVector()) {
+ FindOrCreateVectorType(
+ FindOrCreateFloatType(
+ vector->element_type()->AsInteger()->width()),
+ vector->element_count());
+ } else {
+ FindOrCreateFloatType(type->AsInteger()->width());
+ }
+
+ ApplyTransformation(TransformationEquationInstruction(
+ GetFuzzerContext()->GetFreshId(), opcode,
+ {operand->result_id()}, instruction_descriptor));
+ return;
+ }
+ case SpvOpBitcast: {
+ std::vector<const opt::Instruction*> candidate_instructions;
+ for (const auto* inst : available_instructions) {
+ const auto* type =
+ GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ assert(type && "Instruction has invalid type");
+ if ((type->AsVector() &&
+ (type->AsVector()->element_type()->AsInteger() ||
+ type->AsVector()->element_type()->AsFloat())) ||
+ type->AsInteger() || type->AsFloat()) {
+ // We support OpBitcast for only scalars or vectors of
+ // numerical type.
+ candidate_instructions.push_back(inst);
+ }
+ }
+
+ if (!candidate_instructions.empty()) {
+ const auto* operand_inst =
+ candidate_instructions[GetFuzzerContext()->RandomIndex(
+ candidate_instructions)];
+ const auto* operand_type =
+ GetIRContext()->get_type_mgr()->GetType(
+ operand_inst->type_id());
+ assert(operand_type && "Operand instruction has invalid type");
+
+ // Make sure a result type exists in the module.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+ // The only constraint on the types of OpBitcast's parameters
+ // is that they must have the same number of bits. Consider
+ // improving the code below to support this in full.
+ if (const auto* vector = operand_type->AsVector()) {
+ uint32_t element_type_id;
+ if (const auto* int_type =
+ vector->element_type()->AsInteger()) {
+ element_type_id = FindOrCreateFloatType(int_type->width());
+ } else {
+ assert(vector->element_type()->AsFloat() &&
+ "Vector must have numerical elements");
+ element_type_id = FindOrCreateIntegerType(
+ vector->element_type()->AsFloat()->width(),
+ GetFuzzerContext()->ChooseEven());
+ }
+
+ FindOrCreateVectorType(element_type_id,
+ vector->element_count());
+ } else if (const auto* int_type = operand_type->AsInteger()) {
+ FindOrCreateFloatType(int_type->width());
+ } else {
+ assert(operand_type->AsFloat() &&
+ "Operand is not a scalar of numerical type");
+ FindOrCreateIntegerType(operand_type->AsFloat()->width(),
+ GetFuzzerContext()->ChooseEven());
+ }
+
+ ApplyTransformation(TransformationEquationInstruction(
+ GetFuzzerContext()->GetFreshId(), opcode,
+ {operand_inst->result_id()}, instruction_descriptor));
+ return;
+ }
+ } break;
case SpvOpIAdd:
case SpvOpISub: {
// Instructions of integer (scalar or vector) result type are
@@ -182,6 +279,20 @@
}
std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetFloatInstructions(
+ const std::vector<opt::Instruction*>& instructions) const {
+ std::vector<opt::Instruction*> result;
+ for (auto& inst : instructions) {
+ auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ if (type->AsFloat() ||
+ (type->AsVector() && type->AsVector()->element_type()->AsFloat())) {
+ result.push_back(inst);
+ }
+ }
+ return result;
+}
+
+std::vector<opt::Instruction*>
FuzzerPassAddEquationInstructions::GetBooleanInstructions(
const std::vector<opt::Instruction*>& instructions) const {
std::vector<opt::Instruction*> result;
@@ -225,10 +336,11 @@
if (type->AsVector()) {
type = type->AsVector()->element_type();
}
- assert(type->AsInteger() &&
+ assert((type->AsInteger() || type->AsFloat()) &&
"Precondition: all input instructions must "
- "have integer scalar or vector type.");
- if (type->AsInteger()->width() == bit_width) {
+ "have integer or float scalar or vector type.");
+ if ((type->AsInteger() && type->AsInteger()->width() == bit_width) ||
+ (type->AsFloat() && type->AsFloat()->width() == bit_width)) {
result.push_back(inst);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
index 6e64977..8328b6b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -41,6 +41,11 @@
std::vector<opt::Instruction*> GetIntegerInstructions(
const std::vector<opt::Instruction*>& instructions) const;
+ // Returns only instructions, that have either a scalar floating-point or a
+ // vector type.
+ std::vector<opt::Instruction*> GetFloatInstructions(
+ const std::vector<opt::Instruction*>& instructions) const;
+
// Yields those instructions in |instructions| that have boolean scalar or
// vector result type.
std::vector<opt::Instruction*> GetBooleanInstructions(
@@ -53,9 +58,9 @@
const std::vector<opt::Instruction*>& instructions,
uint32_t vector_width) const;
- // Requires that |instructions| are integer scalars or vectors. Returns only
- // those instructions for which the bit-width of the underlying integer type
- // is |bit_width|.
+ // Requires that |instructions| are integer or float scalars or vectors.
+ // Returns only those instructions for which the bit-width of the underlying
+ // integer or floating-point type is |bit_width|.
std::vector<opt::Instruction*> RestrictToElementBitWidth(
const std::vector<opt::Instruction*>& instructions,
uint32_t bit_width) const;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
index 569df10..b6f4c85 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -106,148 +106,92 @@
});
}
-std::map<uint32_t, std::vector<opt::Instruction*>>
-FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
- opt::Function* function, opt::BasicBlock* block,
- const opt::BasicBlock::iterator& inst_it) {
- // Find all instructions in scope that could potentially be used as actual
- // parameters. Weed out unsuitable pointer arguments immediately.
- std::vector<opt::Instruction*> potentially_suitable_instructions =
- FindAvailableInstructions(
- function, block, inst_it,
- [this, block](opt::IRContext* context,
- opt::Instruction* inst) -> bool {
- if (!inst->HasResultId() || !inst->type_id()) {
- // An instruction needs a result id and type in order
- // to be suitable as an actual parameter.
- return false;
- }
- if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
- SpvOpTypePointer) {
- switch (inst->opcode()) {
- case SpvOpFunctionParameter:
- case SpvOpVariable:
- // Function parameters and variables are the only
- // kinds of pointer that can be used as actual
- // parameters.
- break;
- default:
- return false;
- }
- if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
- block->id()) &&
- !GetTransformationContext()
- ->GetFactManager()
- ->PointeeValueIsIrrelevant(inst->result_id())) {
- // We can only pass a pointer as an actual parameter
- // if the pointee value for the pointer is irrelevant,
- // or if the block from which we would make the
- // function call is dead.
- return false;
- }
- }
- return true;
- });
-
- // Group all the instructions that are potentially viable as function actual
- // parameters by their result types.
- std::map<uint32_t, std::vector<opt::Instruction*>> result;
- for (auto inst : potentially_suitable_instructions) {
- if (result.count(inst->type_id()) == 0) {
- // This is the first instruction of this type we have seen, so populate
- // the map with an entry.
- result.insert({inst->type_id(), {}});
- }
- // Add the instruction to the sequence of instructions already associated
- // with this type.
- result.at(inst->type_id()).push_back(inst);
- }
- return result;
-}
-
std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
const opt::Function& callee, opt::Function* caller_function,
opt::BasicBlock* caller_block,
const opt::BasicBlock::iterator& caller_inst_it) {
- auto type_to_available_instructions =
- GetAvailableInstructionsSuitableForActualParameters(
- caller_function, caller_block, caller_inst_it);
-
- opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef(
- callee.DefInst().GetSingleWordInOperand(1));
- assert(function_type->opcode() == SpvOpTypeFunction &&
- "The function type does not have the expected opcode.");
- std::vector<uint32_t> result;
- for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands();
- arg_index++) {
- auto arg_type_id =
- GetIRContext()
- ->get_def_use_mgr()
- ->GetDef(function_type->GetSingleWordInOperand(arg_index))
- ->result_id();
- if (type_to_available_instructions.count(arg_type_id)) {
- std::vector<opt::Instruction*>& candidate_arguments =
- type_to_available_instructions.at(arg_type_id);
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value
- // selected here is arbitrary. We should consider adding this
- // information as a fact so that the passed parameter could be
- // transformed/changed.
- result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex(
- candidate_arguments)]
- ->result_id());
- } else {
- // We don't have a suitable id in scope to pass, so we must make
- // something up.
- auto type_instruction =
- GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id);
-
- if (type_instruction->opcode() == SpvOpTypePointer) {
- // In the case of a pointer, we make a new variable, at function
- // or global scope depending on the storage class of the
- // pointer.
-
- // Get a fresh id for the new variable.
- uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
-
- // The id of this variable is what we pass as the parameter to
- // the call.
- result.push_back(fresh_variable_id);
-
- // Now bring the variable into existence.
- auto storage_class = static_cast<SpvStorageClass>(
- type_instruction->GetSingleWordInOperand(0));
- if (storage_class == SpvStorageClassFunction) {
- // Add a new zero-initialized local variable to the current
- // function, noting that its pointee value is irrelevant.
- ApplyTransformation(TransformationAddLocalVariable(
- fresh_variable_id, arg_type_id, caller_function->result_id(),
- FindOrCreateZeroConstant(
- type_instruction->GetSingleWordInOperand(1)),
- true));
- } else {
- assert((storage_class == SpvStorageClassPrivate ||
- storage_class == SpvStorageClassWorkgroup) &&
- "Only Function, Private and Workgroup storage classes are "
- "supported at present.");
- // Add a new global variable to the module, zero-initializing it if
- // it has Private storage class, and noting that its pointee value is
- // irrelevant.
- ApplyTransformation(TransformationAddGlobalVariable(
- fresh_variable_id, arg_type_id, storage_class,
- storage_class == SpvStorageClassPrivate
- ? FindOrCreateZeroConstant(
- type_instruction->GetSingleWordInOperand(1))
- : 0,
- true));
+ auto available_pointers = FindAvailableInstructions(
+ caller_function, caller_block, caller_inst_it,
+ [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) {
+ if (inst->opcode() != SpvOpVariable ||
+ inst->opcode() != SpvOpFunctionParameter) {
+ // Function parameters and variables are the only
+ // kinds of pointer that can be used as actual
+ // parameters.
+ return false;
}
- } else {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use
- // constant zero for the parameter, but could consider adding a fact
- // to allow further passes to obfuscate it.
- result.push_back(FindOrCreateZeroConstant(arg_type_id));
- }
+
+ return GetTransformationContext()->GetFactManager()->BlockIsDead(
+ caller_block->id()) ||
+ GetTransformationContext()
+ ->GetFactManager()
+ ->PointeeValueIsIrrelevant(inst->result_id());
+ });
+
+ std::unordered_map<uint32_t, std::vector<uint32_t>> type_id_to_result_id;
+ for (const auto* inst : available_pointers) {
+ type_id_to_result_id[inst->type_id()].push_back(inst->result_id());
+ }
+
+ std::vector<uint32_t> result;
+ for (const auto* param :
+ fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) {
+ const auto* param_type =
+ GetIRContext()->get_type_mgr()->GetType(param->type_id());
+ assert(param_type && "Parameter has invalid type");
+
+ if (!param_type->AsPointer()) {
+ // We mark the constant as irrelevant so that we can replace it with a
+ // more interesting value later.
+ result.push_back(FindOrCreateZeroConstant(param->type_id(), true));
+ continue;
+ }
+
+ if (type_id_to_result_id.count(param->type_id())) {
+ // Use an existing pointer if there are any.
+ const auto& candidates = type_id_to_result_id[param->type_id()];
+ result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]);
+ continue;
+ }
+
+ // Make a new variable, at function or global scope depending on the storage
+ // class of the pointer.
+
+ // Get a fresh id for the new variable.
+ uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
+
+ // The id of this variable is what we pass as the parameter to
+ // the call.
+ result.push_back(fresh_variable_id);
+ type_id_to_result_id[param->type_id()].push_back(fresh_variable_id);
+
+ // Now bring the variable into existence.
+ auto storage_class = param_type->AsPointer()->storage_class();
+ auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ GetIRContext(), param->type_id());
+ if (storage_class == SpvStorageClassFunction) {
+ // Add a new zero-initialized local variable to the current
+ // function, noting that its pointee value is irrelevant.
+ ApplyTransformation(TransformationAddLocalVariable(
+ fresh_variable_id, param->type_id(), caller_function->result_id(),
+ FindOrCreateZeroConstant(pointee_type_id, false), true));
+ } else {
+ assert((storage_class == SpvStorageClassPrivate ||
+ storage_class == SpvStorageClassWorkgroup) &&
+ "Only Function, Private and Workgroup storage classes are "
+ "supported at present.");
+ // Add a new global variable to the module, zero-initializing it if
+ // it has Private storage class, and noting that its pointee value is
+ // irrelevant.
+ ApplyTransformation(TransformationAddGlobalVariable(
+ fresh_variable_id, param->type_id(), storage_class,
+ storage_class == SpvStorageClassPrivate
+ ? FindOrCreateZeroConstant(pointee_type_id, false)
+ : 0,
+ true));
}
}
+
return result;
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h
index 8f75e8c..4ed8791 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.h
@@ -34,14 +34,6 @@
void Apply() override;
private:
- // Identify all instructions available at |instr_it|, in block |block| of
- // |function|, that are potentially suitable as function call actual
- // parameters. The results are grouped by type.
- std::map<uint32_t, std::vector<opt::Instruction*>>
- GetAvailableInstructionsSuitableForActualParameters(
- opt::Function* function, opt::BasicBlock* block,
- const opt::BasicBlock::iterator& inst_it);
-
// Randomly chooses suitable arguments to invoke |callee| right before
// instruction |caller_inst_it| of block |caller_block| in |caller_function|,
// based on both existing available instructions and the addition of new
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
index 4023b22..9a45a37 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
@@ -30,8 +30,22 @@
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
void FuzzerPassAddGlobalVariables::Apply() {
+ SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
+ for (auto& entry_point : GetIRContext()->module()->entry_points()) {
+ // If the execution model of some entry point is GLCompute,
+ // then the variable storage class may be Workgroup.
+ if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) {
+ variable_storage_class =
+ GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass())
+ ? SpvStorageClassWorkgroup
+ : SpvStorageClassPrivate;
+ break;
+ }
+ }
+
auto basic_type_ids_and_pointers =
- GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
+ GetAvailableBasicTypesAndPointers(variable_storage_class);
// These are the basic types that are available to this fuzzer pass.
auto& basic_types = basic_type_ids_and_pointers.first;
@@ -59,18 +73,21 @@
pointer_type_id = GetFuzzerContext()->GetFreshId();
available_pointers_to_basic_type.push_back(pointer_type_id);
ApplyTransformation(TransformationAddTypePointer(
- pointer_type_id, SpvStorageClassPrivate, basic_type));
+ pointer_type_id, variable_storage_class, basic_type));
} else {
// There is - grab one.
pointer_type_id =
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
available_pointers_to_basic_type)];
}
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274): We could
- // add new variables with Workgroup storage class in compute shaders.
+
ApplyTransformation(TransformationAddGlobalVariable(
GetFuzzerContext()->GetFreshId(), pointer_type_id,
- SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true));
+ variable_storage_class,
+ variable_storage_class == SpvStorageClassPrivate
+ ? FindOrCreateZeroConstant(basic_type, false)
+ : 0,
+ true));
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..313bb0d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+#include "source/fuzz/transformation_composite_construct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddImageSampleUnusedComponents::
+ FuzzerPassAddImageSampleUnusedComponents(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddImageSampleUnusedComponents::
+ ~FuzzerPassAddImageSampleUnusedComponents() = default;
+
+void FuzzerPassAddImageSampleUnusedComponents::Apply() {
+ // SPIR-V module to help understand the transformation.
+ //
+ // OpCapability Shader
+ // %1 = OpExtInstImport "GLSL.std.450"
+ // OpMemoryModel Logical GLSL450
+ // OpEntryPoint Fragment %15 "main" %12 %14
+ // OpExecutionMode %15 OriginUpperLeft
+ //
+ // ; Decorations
+ // OpDecorate %12 Location 0 ; Input color variable location
+ // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
+ // descriptor set OpDecorate %13 Binding 0 ; Image coordinate
+ // variable binding OpDecorate %14 Location 0 ; Fragment color
+ // variable location
+ //
+ // ; Types
+ // %2 = OpTypeVoid
+ // %3 = OpTypeFunction %2
+ // %4 = OpTypeFloat 32
+ // %5 = OpTypeVector %4 2
+ // %6 = OpTypeVector %4 4
+ // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ // %8 = OpTypeSampledImage %7
+ // %9 = OpTypePointer Input %5
+ // %10 = OpTypePointer UniformConstant %8
+ // %11 = OpTypePointer Output %6
+ //
+ // ; Variables
+ // %12 = OpVariable %9 Input ; Input image coordinate variable
+ // %13 = OpVariable %10 UniformConstant ; Image variable
+ // %14 = OpVariable %11 Output ; Fragment color variable
+ //
+ // ; main function
+ // %15 = OpFunction %2 None %3
+ // %16 = OpLabel
+ // %17 = OpLoad %5 %12
+ // %18 = OpLoad %8 %13
+ // %19 = OpImageSampleImplicitLod %6 %18 %17
+ // OpStore %14 %19
+ // OpReturn
+ // OpFunctionEnd
+
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
+ if (!spvOpcodeIsImageSample(instruction->opcode())) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfAddingImageSampleUnusedComponents())) {
+ return;
+ }
+
+ // Gets image sample coordinate information.
+ // |coordinate_instruction| %17 = OpLoad %5 %12
+ uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
+ auto coordinate_instruction =
+ GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
+ auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+ coordinate_instruction->type_id());
+
+ // If the coordinate is a 4-dimensional vector, then no unused components
+ // may be added.
+ if (coordinate_type->AsVector() &&
+ coordinate_type->AsVector()->element_count() == 4) {
+ return;
+ }
+
+ // If the coordinate is a scalar, then at most 3 unused components may be
+ // added. If the coordinate is a vector, then the maximum number of unused
+ // components depends on the vector size.
+ // For the sample module, the coordinate type instruction is %5 =
+ // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
+ uint32_t max_unused_component_count =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? 3
+ : 4 - coordinate_type->AsVector()->element_count();
+
+ // |unused_component_count| may be 1 or 2.
+ uint32_t unused_component_count =
+ GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
+ max_unused_component_count);
+
+ // Gets a type for the zero-unused components.
+ uint32_t zero_constant_type_id;
+ switch (unused_component_count) {
+ case 1:
+ // If the coordinate is an integer or float, then the unused components
+ // type is the same as the coordinate. If the coordinate is a vector,
+ // then the unused components type is the same as the vector components
+ // type.
+ zero_constant_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? coordinate_instruction->type_id()
+ : GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type());
+ break;
+ case 2:
+ case 3:
+ // If the coordinate is an integer or float, then the unused components
+ // type is the same as the coordinate. If the coordinate is a vector,
+ // then the unused components type is the same as the coordinate
+ // components type.
+ // |zero_constant_type_id| %5 = OpTypeVector %4 2
+ zero_constant_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+ unused_component_count)
+ : FindOrCreateVectorType(
+ GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type()),
+ unused_component_count);
+ break;
+ default:
+ assert(false && "Should be unreachable.");
+ zero_constant_type_id = 0;
+ break;
+ }
+
+ // Gets |coordinate_type| again because the module may have changed due to
+ // the use of FindOrCreateVectorType above.
+ coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+ coordinate_instruction->type_id());
+
+ // If the new vector type with unused components does not exist, then create
+ // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
+ uint32_t coordinate_with_unused_components_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+ 1 + unused_component_count)
+ : FindOrCreateVectorType(
+ GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type()),
+ coordinate_type->AsVector()->element_count() +
+ unused_component_count);
+
+ // Inserts an OpCompositeConstruct instruction which
+ // represents the coordinate with unused components.
+ // |coordinate_with_unused_components_id|
+ // %22 = OpCompositeConstruct %6 %17 %21
+ uint32_t coordinate_with_unused_components_id =
+ GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationCompositeConstruct(
+ coordinate_with_unused_components_type_id,
+ {coordinate_instruction->result_id(),
+ // FindOrCreateZeroConstant
+ // %20 = OpConstant %4 0
+ // %21 = OpConstantComposite %5 %20 %20
+ FindOrCreateZeroConstant(zero_constant_type_id, false)},
+ MakeInstructionDescriptor(GetIRContext(), instruction),
+ coordinate_with_unused_components_id));
+
+ // Tries to add unused components to the image sample coordinate.
+ // %19 = OpImageSampleImplicitLod %6 %18 %22
+ ApplyTransformation(TransformationAddImageSampleUnusedComponents(
+ coordinate_with_unused_components_id,
+ MakeInstructionDescriptor(GetIRContext(), instruction)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
new file mode 100644
index 0000000..26374c3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for image sample instructions in the module and
+// randomly applies the transformation to add unused components to the image
+// sample coordinate.
+class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
+ public:
+ FuzzerPassAddImageSampleUnusedComponents(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddImageSampleUnusedComponents();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
index 661159e..ef8b5d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
@@ -71,7 +71,7 @@
}
ApplyTransformation(TransformationAddLocalVariable(
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
- FindOrCreateZeroConstant(basic_type), true));
+ FindOrCreateZeroConstant(basic_type, false), true));
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp
new file mode 100644
index 0000000..c5c9c33
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_parameters.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_parameter.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddParameters::FuzzerPassAddParameters(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
+
+void FuzzerPassAddParameters::Apply() {
+ // Compute type candidates for the new parameter.
+ std::vector<uint32_t> type_candidates;
+ for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
+ const auto* type =
+ GetIRContext()->get_type_mgr()->GetType(type_inst->result_id());
+ assert(type && "Type instruction is not registered in the type manager");
+ if (TransformationAddParameter::IsParameterTypeSupported(*type)) {
+ type_candidates.push_back(type_inst->result_id());
+ }
+ }
+
+ if (type_candidates.empty()) {
+ // The module contains no suitable types to use in new parameters.
+ return;
+ }
+
+ // Iterate over all functions in the module.
+ for (const auto& function : *GetIRContext()->module()) {
+ // Skip all entry-point functions - we don't want to change those.
+ if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+ function.result_id())) {
+ continue;
+ }
+
+ if (GetNumberOfParameters(function) >=
+ GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
+ continue;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingParameters())) {
+ continue;
+ }
+
+ auto num_new_parameters =
+ GetFuzzerContext()->GetRandomNumberOfNewParameters(
+ GetNumberOfParameters(function));
+ for (uint32_t i = 0; i < num_new_parameters; ++i) {
+ ApplyTransformation(TransformationAddParameter(
+ function.result_id(), GetFuzzerContext()->GetFreshId(),
+ // We mark the constant as irrelevant so that we can replace it with a
+ // more interesting value later.
+ FindOrCreateZeroConstant(
+ type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)],
+ true),
+ GetFuzzerContext()->GetFreshId()));
+ }
+ }
+}
+
+uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
+ const opt::Function& function) const {
+ const auto* type = GetIRContext()->get_type_mgr()->GetType(
+ function.DefInst().GetSingleWordInOperand(1));
+ assert(type && type->AsFunction());
+
+ return static_cast<uint32_t>(type->AsFunction()->param_types().size());
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h
new file mode 100644
index 0000000..f1261ae
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_parameters.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each non-entry-point function in the module whether to
+// add new parameters to it. If so, randomly determines the number of parameters
+// to add, their type and creates constants used to initialize them.
+class FuzzerPassAddParameters : public FuzzerPass {
+ public:
+ FuzzerPassAddParameters(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddParameters() override;
+
+ void Apply() override;
+
+ private:
+ // Returns number of parameters of |function|.
+ uint32_t GetNumberOfParameters(const opt::Function& function) const;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
new file mode 100644
index 0000000..a2497df
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default;
+
+void FuzzerPassAddRelaxedDecorations::Apply() {
+ // Consider every instruction in every block in every function.
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ for (auto& inst : block) {
+ // Randomly choose whether to apply the RelaxedPrecision decoration
+ // to this instruction.
+ if (GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) {
+ TransformationAddRelaxedDecoration transformation(inst.result_id());
+ // Restrict attention to numeric instructions (returning 32-bit
+ // floats or ints according to SPIR-V documentation) in dead blocks.
+ if (transformation.IsApplicable(GetIRContext(),
+ *GetTransformationContext())) {
+ transformation.Apply(GetIRContext(), GetTransformationContext());
+ *GetTransformations()->add_transformation() =
+ transformation.ToMessage();
+ }
+ }
+ }
+ }
+ }
+}
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
new file mode 100644
index 0000000..dad5dfc
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+#define SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that applies the Relaxed decoration to numeric instructions.
+class FuzzerPassAddRelaxedDecorations : public FuzzerPass {
+ public:
+ FuzzerPassAddRelaxedDecorations(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddRelaxedDecorations() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
new file mode 100644
index 0000000..f109174
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_synonyms.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
+
+void FuzzerPassAddSynonyms::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ // Skip |inst_it| if we can't insert anything above it. OpIAdd is just
+ // a representative of some instruction that might be produced by the
+ // transformation.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingSynonyms())) {
+ return;
+ }
+
+ auto synonym_type = GetFuzzerContext()->GetRandomSynonymType();
+
+ // Select all instructions that can be used to create a synonym to.
+ auto available_instructions = FindAvailableInstructions(
+ function, block, inst_it,
+ [synonym_type, this](opt::IRContext* ir_context,
+ opt::Instruction* inst) {
+ // Check that we can create a synonym to |inst| as described by
+ // the |synonym_type| and insert it before |inst_it|.
+ return TransformationAddSynonym::IsInstructionValid(
+ ir_context, *GetTransformationContext(), inst, synonym_type);
+ });
+
+ if (available_instructions.empty()) {
+ return;
+ }
+
+ const auto* existing_synonym =
+ available_instructions[GetFuzzerContext()->RandomIndex(
+ available_instructions)];
+
+ // Make sure the module contains all instructions required to apply the
+ // transformation.
+ switch (synonym_type) {
+ case protobufs::TransformationAddSynonym::ADD_ZERO:
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ case protobufs::TransformationAddSynonym::LOGICAL_OR:
+ // Create a zero constant to be used as an operand of the synonymous
+ // instruction.
+ FindOrCreateZeroConstant(existing_synonym->type_id(), false);
+ break;
+ case protobufs::TransformationAddSynonym::MUL_ONE:
+ case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+ const auto* existing_synonym_type =
+ GetIRContext()->get_type_mgr()->GetType(
+ existing_synonym->type_id());
+ assert(existing_synonym_type && "Instruction has invalid type");
+
+ if (const auto* vector = existing_synonym_type->AsVector()) {
+ auto element_type_id =
+ GetIRContext()->get_type_mgr()->GetId(vector->element_type());
+ assert(element_type_id && "Vector's element type is invalid");
+
+ auto one_word = vector->element_type()->AsFloat()
+ ? fuzzerutil::FloatToWord(1)
+ : 1u;
+ FindOrCreateCompositeConstant(
+ std::vector<uint32_t>(
+ vector->element_count(),
+ FindOrCreateConstant({one_word}, element_type_id, false)),
+ existing_synonym->type_id(), false);
+ } else {
+ FindOrCreateConstant(
+ {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
+ : 1u},
+ existing_synonym->type_id(), false);
+ }
+ } break;
+ default:
+ // This assertion will fail if some SynonymType is missing from the
+ // switch statement.
+ assert(
+ !TransformationAddSynonym::IsAdditionalConstantRequired(
+ synonym_type) &&
+ "|synonym_type| requires an additional constant to be present "
+ "in the module");
+ break;
+ }
+
+ ApplyTransformation(TransformationAddSynonym(
+ existing_synonym->result_id(), synonym_type,
+ GetFuzzerContext()->GetFreshId(), instruction_descriptor));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h
new file mode 100644
index 0000000..dcfb938
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_synonyms.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Sprinkles instructions through the module that produce ids, synonymous to
+// some other instructions.
+class FuzzerPassAddSynonyms : public FuzzerPass {
+ public:
+ FuzzerPassAddSynonyms(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddSynonyms() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
new file mode 100644
index 0000000..453448b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -0,0 +1,145 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddVectorShuffleInstructions::
+ ~FuzzerPassAddVectorShuffleInstructions() = default;
+
+void FuzzerPassAddVectorShuffleInstructions::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator instruction_iterator,
+ const protobufs::InstructionDescriptor& instruction_descriptor)
+ -> void {
+ assert(instruction_iterator->opcode() ==
+ instruction_descriptor.target_instruction_opcode() &&
+ "The opcode of the instruction we might insert before must be "
+ "the same as the opcode in the descriptor for the instruction");
+
+ // Randomly decide whether to try adding an OpVectorShuffle instruction.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) {
+ return;
+ }
+
+ // It must be valid to insert an OpVectorShuffle instruction
+ // before |instruction_iterator|.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpVectorShuffle, instruction_iterator)) {
+ return;
+ }
+
+ // Looks for vectors that we might consider to use as OpVectorShuffle
+ // operands.
+ std::vector<opt::Instruction*> vector_instructions =
+ FindAvailableInstructions(
+ function, block, instruction_iterator,
+ [this, instruction_descriptor](
+ opt::IRContext* ir_context,
+ opt::Instruction* instruction) -> bool {
+ if (!instruction->result_id() || !instruction->type_id()) {
+ return false;
+ }
+
+ if (!ir_context->get_type_mgr()
+ ->GetType(instruction->type_id())
+ ->AsVector()) {
+ return false;
+ }
+
+ if (!GetTransformationContext()
+ ->GetFactManager()
+ ->IdIsIrrelevant(instruction->result_id()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context,
+ *GetTransformationContext(),
+ instruction)) {
+ // If the id is irrelevant, we can use it since it will not
+ // participate in DataSynonym fact. Otherwise, we should be
+ // able to produce a synonym out of the id.
+ return false;
+ }
+
+ return fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context,
+ FindInstruction(instruction_descriptor, ir_context),
+ instruction->result_id());
+ });
+
+ // If there are no vector instructions, then return.
+ if (vector_instructions.empty()) {
+ return;
+ }
+
+ auto vector_1_instruction =
+ vector_instructions[GetFuzzerContext()->RandomIndex(
+ vector_instructions)];
+ auto vector_1_type = GetIRContext()
+ ->get_type_mgr()
+ ->GetType(vector_1_instruction->type_id())
+ ->AsVector();
+
+ auto vector_2_instruction =
+ GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+ auto vector_2_type = GetIRContext()
+ ->get_type_mgr()
+ ->GetType(vector_2_instruction->type_id())
+ ->AsVector();
+
+ // |vector_1| and |vector_2| must have the same element type as each
+ // other. The loop is guaranteed to terminate because each iteration
+ // removes on possible choice for |vector_2|, and there is at least one
+ // choice that will cause the loop to exit - namely |vector_1|.
+ while (vector_1_type->element_type() != vector_2_type->element_type()) {
+ vector_2_instruction =
+ GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+ vector_2_type = GetIRContext()
+ ->get_type_mgr()
+ ->GetType(vector_2_instruction->type_id())
+ ->AsVector();
+ }
+
+ // Gets components and creates the appropriate result vector type.
+ std::vector<uint32_t> components =
+ GetFuzzerContext()->GetRandomComponentsForVectorShuffle(
+ vector_1_type->element_count() +
+ vector_2_type->element_count());
+ FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId(
+ vector_1_type->element_type()),
+ static_cast<uint32_t>(components.size()));
+
+ // Applies the vector shuffle transformation.
+ ApplyTransformation(TransformationVectorShuffle(
+ instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+ vector_1_instruction->result_id(),
+ vector_2_instruction->result_id(), components));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
new file mode 100644
index 0000000..99b9f24
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Adds OpVectorShuffle instructions to the module.
+class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass {
+ public:
+ FuzzerPassAddVectorShuffleInstructions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddVectorShuffleInstructions();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index 32f5ea5..68f0ca7 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -75,7 +75,7 @@
*inst_it, mask_index);
auto existing_mask =
existing_mask_in_operand_index < inst_it->NumInOperands()
- ? inst_it->GetSingleWordOperand(
+ ? inst_it->GetSingleWordInOperand(
existing_mask_in_operand_index)
: static_cast<uint32_t>(SpvMemoryAccessMaskNone);
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 0ec93e1..2808ad5 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -73,10 +73,9 @@
continue;
}
// |use_index| is the absolute index of the operand. We require
- // the index of the operand restricted to input operands only, so
- // we subtract the number of non-input operands from |use_index|.
+ // the index of the operand restricted to input operands only.
uint32_t use_in_operand_index =
- use_index - use_inst->NumOperands() + use_inst->NumInOperands();
+ fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
GetIRContext(), use_inst, use_in_operand_index)) {
continue;
@@ -143,6 +142,9 @@
: parent_block->terminator();
}
+ assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+ synonym_to_try->object()) &&
+ "Irrelevant ids can't participate in DataSynonym facts");
ApplyTransformation(TransformationCompositeExtract(
MakeInstructionDescriptor(GetIRContext(),
instruction_to_insert_before),
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
index e78f8ec..6443e89 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -14,12 +14,10 @@
#include "source/fuzz/fuzzer_pass_construct_composites.h"
-#include <cmath>
#include <memory>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_composite_construct.h"
-#include "source/util/make_unique.h"
namespace spvtools {
namespace fuzz {
@@ -67,8 +65,23 @@
// program point) and suitable for making a synonym of, associate it
// with the id of its result type.
TypeIdToInstructions type_id_to_available_instructions;
- for (auto instruction : FindAvailableInstructions(
- function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
+ auto available_instructions = FindAvailableInstructions(
+ function, block, inst_it,
+ [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+ if (!inst->result_id() || !inst->type_id()) {
+ return false;
+ }
+
+ // If the id is irrelevant, we can use it since it will not
+ // participate in DataSynonym fact. Otherwise, we should be able
+ // to produce a synonym out of the id.
+ return GetTransformationContext()
+ ->GetFactManager()
+ ->IdIsIrrelevant(inst->result_id()) ||
+ fuzzerutil::CanMakeSynonymOf(
+ ir_context, *GetTransformationContext(), inst);
+ });
+ for (auto instruction : available_instructions) {
RecordAvailableInstruction(instruction,
&type_id_to_available_instructions);
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
index f055b59..81326ac 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -15,7 +15,8 @@
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_add_synonym.h"
namespace spvtools {
namespace fuzz {
@@ -54,8 +55,12 @@
}
std::vector<opt::Instruction*> relevant_instructions =
- FindAvailableInstructions(function, block, inst_it,
- fuzzerutil::CanMakeSynonymOf);
+ FindAvailableInstructions(
+ function, block, inst_it,
+ [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+ return fuzzerutil::CanMakeSynonymOf(
+ ir_context, *GetTransformationContext(), inst);
+ });
// At this point, |relevant_instructions| contains all the instructions
// we might think of copying.
@@ -65,11 +70,12 @@
// Choose a copyable instruction at random, and create and apply an
// object copying transformation based on it.
- ApplyTransformation(TransformationCopyObject(
+ ApplyTransformation(TransformationAddSynonym(
relevant_instructions[GetFuzzerContext()->RandomIndex(
relevant_instructions)]
->result_id(),
- instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+ protobufs::TransformationAddSynonym::COPY_OBJECT,
+ GetFuzzerContext()->GetFreshId(), instruction_descriptor));
});
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
index b907376..aa0e243 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -27,6 +27,7 @@
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_spec_constant_op.h"
#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
@@ -326,8 +327,9 @@
new_result_id = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddTypeArray(
new_result_id, original_id_to_donated_id->at(component_type_id),
- FindOrCreate32BitIntegerConstant(
- GetFuzzerContext()->GetRandomSizeForNewArray(), false)));
+ FindOrCreateIntegerConstant(
+ {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false,
+ false)));
} break;
case SpvOpTypeStruct: {
// Similar to SpvOpTypeArray.
@@ -413,14 +415,41 @@
argument_type_ids));
}
} break;
+ case SpvOpSpecConstantOp: {
+ new_result_id = GetFuzzerContext()->GetFreshId();
+ auto type_id = original_id_to_donated_id->at(type_or_value.type_id());
+ auto opcode = static_cast<SpvOp>(type_or_value.GetSingleWordInOperand(0));
+
+ // Make sure we take into account |original_id_to_donated_id| when
+ // computing operands for OpSpecConstantOp.
+ opt::Instruction::OperandList operands;
+ for (uint32_t i = 1; i < type_or_value.NumInOperands(); ++i) {
+ const auto& operand = type_or_value.GetInOperand(i);
+ auto data =
+ operand.type == SPV_OPERAND_TYPE_ID
+ ? opt::Operand::OperandData{original_id_to_donated_id->at(
+ operand.words[0])}
+ : operand.words;
+
+ operands.push_back({operand.type, std::move(data)});
+ }
+
+ ApplyTransformation(TransformationAddSpecConstantOp(
+ new_result_id, type_id, opcode, std::move(operands)));
+ } break;
+ case SpvOpSpecConstantTrue:
+ case SpvOpSpecConstantFalse:
case SpvOpConstantTrue:
case SpvOpConstantFalse: {
// It is OK to have duplicate definitions of True and False, so add
// these to the module, using a remapped Bool type.
new_result_id = GetFuzzerContext()->GetFreshId();
- ApplyTransformation(TransformationAddConstantBoolean(
- new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
+ auto value = type_or_value.opcode() == SpvOpConstantTrue ||
+ type_or_value.opcode() == SpvOpSpecConstantTrue;
+ ApplyTransformation(
+ TransformationAddConstantBoolean(new_result_id, value, false));
} break;
+ case SpvOpSpecConstant:
case SpvOpConstant: {
// It is OK to have duplicate constant definitions, so add this to the
// module using a remapped result type.
@@ -431,8 +460,9 @@
});
ApplyTransformation(TransformationAddConstantScalar(
new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
- data_words));
+ data_words, false));
} break;
+ case SpvOpSpecConstantComposite:
case SpvOpConstantComposite: {
assert(original_id_to_donated_id->count(type_or_value.type_id()) &&
"Composite types for which it is possible to create a constant "
@@ -453,7 +483,7 @@
});
ApplyTransformation(TransformationAddConstantComposite(
new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
- constituent_ids));
+ constituent_ids, false));
} break;
case SpvOpConstantNull: {
if (!original_id_to_donated_id->count(type_or_value.type_id())) {
@@ -515,7 +545,8 @@
? 0
: FindOrCreateZeroConstant(
fuzzerutil::GetPointeeTypeIdFromPointerType(
- GetIRContext(), remapped_pointer_type));
+ GetIRContext(), remapped_pointer_type),
+ false);
} else {
// The variable already had an initializer; use its remapped id.
initializer_id = original_id_to_donated_id->at(
@@ -890,11 +921,10 @@
// We find or add a zero constant to the receiving module for the type in
// question, and add an OpCopyObject instruction that copies this zero.
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
- // Using this particular constant is arbitrary, so if we have a
- // mechanism for noting that an id use is arbitrary and could be
- // fuzzed we should use it here.
- auto zero_constant = FindOrCreateZeroConstant(remapped_type_id);
+ //
+ // We mark the constant as irrelevant so that we can replace it with a
+ // more interesting value later.
+ auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true);
donated_instructions->push_back(MakeInstructionMessage(
SpvOpCopyObject, remapped_type_id,
original_id_to_donated_id->at(instruction.result_id()),
@@ -951,9 +981,11 @@
// This is an uninitialized local variable. Initialize it to zero.
input_operands.push_back(
{SPV_OPERAND_TYPE_ID,
- {FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType(
- GetIRContext(),
- original_id_to_donated_id->at(instruction.type_id())))}});
+ {FindOrCreateZeroConstant(
+ fuzzerutil::GetPointeeTypeIdFromPointerType(
+ GetIRContext(),
+ original_id_to_donated_id->at(instruction.type_id())),
+ false)}});
}
if (instruction.result_id() &&
@@ -982,19 +1014,19 @@
// Various types and constants must be in place for a function to be made
// live-safe. Add them if not already present.
FindOrCreateBoolType(); // Needed for comparisons
- FindOrCreatePointerTo32BitIntegerType(
- false, SpvStorageClassFunction); // Needed for adding loop limiters
- FindOrCreate32BitIntegerConstant(
- 0, false); // Needed for initializing loop limiters
- FindOrCreate32BitIntegerConstant(
- 1, false); // Needed for incrementing loop limiters
+ FindOrCreatePointerToIntegerType(
+ 32, false, SpvStorageClassFunction); // Needed for adding loop limiters
+ FindOrCreateIntegerConstant({0}, 32, false,
+ false); // Needed for initializing loop limiters
+ FindOrCreateIntegerConstant({1}, 32, false,
+ false); // Needed for incrementing loop limiters
// Get a fresh id for the variable that will be used as a loop limiter.
const uint32_t loop_limiter_variable_id = GetFuzzerContext()->GetFreshId();
// Choose a random loop limit, and add the required constant to the
// module if not already there.
- const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
- GetFuzzerContext()->GetRandomLoopLimit(), false);
+ const uint32_t loop_limit = FindOrCreateIntegerConstant(
+ {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false);
// Consider every loop header in the function to donate, and create a
// structure capturing the ids to be used for manipulating the loop
@@ -1068,11 +1100,11 @@
"A runtime array type in the donor should have been "
"replaced by a fixed-sized array in the recipient.");
// The size of this fixed-size array is a suitable bound.
- bound = TransformationAddFunction::GetBoundForCompositeIndex(
- GetIRContext(), *fixed_size_array_type);
+ bound = fuzzerutil::GetBoundForCompositeIndex(
+ *fixed_size_array_type, GetIRContext());
} else {
- bound = TransformationAddFunction::GetBoundForCompositeIndex(
- donor_ir_context, *should_be_composite_type);
+ bound = fuzzerutil::GetBoundForCompositeIndex(
+ *should_be_composite_type, donor_ir_context);
}
const uint32_t index_id = inst.GetSingleWordInOperand(index);
auto index_inst =
@@ -1080,7 +1112,6 @@
auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef(
index_inst->type_id());
assert(index_type_inst->opcode() == SpvOpTypeInt);
- assert(index_type_inst->GetSingleWordInOperand(0) == 32);
opt::analysis::Integer* index_int_type =
donor_ir_context->get_type_mgr()
->GetType(index_type_inst->result_id())
@@ -1089,8 +1120,8 @@
// We will have to clamp this index, so we need a constant
// whose value is one less than the bound, to compare
// against and to use as the clamped value.
- FindOrCreate32BitIntegerConstant(bound - 1,
- index_int_type->IsSigned());
+ FindOrCreateIntegerConstant({bound - 1}, 32,
+ index_int_type->IsSigned(), false);
}
should_be_composite_type =
TransformationAddFunction::FollowCompositeIndex(
@@ -1119,7 +1150,8 @@
assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
"Function return type must not be a pointer.");
kill_unreachable_return_value_id = FindOrCreateZeroConstant(
- original_id_to_donated_id.at(function_return_type_inst->result_id()));
+ original_id_to_donated_id.at(function_return_type_inst->result_id()),
+ false);
}
// Add the function in a livesafe manner.
ApplyTransformation(TransformationAddFunction(
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
new file mode 100644
index 0000000..727132e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassInterchangeZeroLikeConstants::
+ ~FuzzerPassInterchangeZeroLikeConstants() = default;
+
+uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
+ opt::Instruction* declaration) {
+ auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+ declaration->result_id());
+
+ // This pass only toggles zero-like constants
+ if (!constant->IsZero()) {
+ return 0;
+ }
+
+ if (constant->AsScalarConstant()) {
+ return FindOrCreateNullConstant(declaration->type_id());
+ } else if (constant->AsNullConstant()) {
+ // Add declaration of equivalent scalar constant
+ auto kind = constant->type()->kind();
+ if (kind == opt::analysis::Type::kBool ||
+ kind == opt::analysis::Type::kInteger ||
+ kind == opt::analysis::Type::kFloat) {
+ return FindOrCreateZeroConstant(declaration->type_id(), false);
+ }
+ }
+
+ return 0;
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
+ opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+ uses_to_replace) {
+ // Only consider this use if it is in a block
+ if (!GetIRContext()->get_instr_block(use_inst)) {
+ return;
+ }
+
+ // Get the index of the operand restricted to input operands.
+ uint32_t in_operand_index =
+ fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
+ auto id_use_descriptor =
+ MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
+ uses_to_replace->emplace_back(
+ std::make_pair(id_use_descriptor, replacement_id));
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::Apply() {
+ // Make vector keeping track of all the uses we want to replace.
+ // This is a vector of pairs, where the first element is an id use descriptor
+ // identifying the use of a constant id and the second is the id that should
+ // be used to replace it.
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
+
+ for (auto constant : GetIRContext()->GetConstants()) {
+ uint32_t constant_id = constant->result_id();
+ if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+ constant_id)) {
+ continue;
+ }
+
+ uint32_t toggled_id = FindOrCreateToggledConstant(constant);
+ if (!toggled_id) {
+ // Not a zero-like constant
+ continue;
+ }
+
+ assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+ toggled_id) &&
+ "FindOrCreateToggledConstant can't produce an irrelevant id");
+
+ // Record synonymous constants
+ ApplyTransformation(
+ TransformationRecordSynonymousConstants(constant_id, toggled_id));
+
+ // Find all the uses of the constant and, for each, probabilistically
+ // decide whether to replace it.
+ GetIRContext()->get_def_use_mgr()->ForEachUse(
+ constant_id,
+ [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
+ uint32_t use_index) -> void {
+ if (GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfInterchangingZeroLikeConstants())) {
+ MaybeAddUseToReplace(use_inst, use_index, toggled_id,
+ &uses_to_replace);
+ }
+ });
+ }
+
+ // Replace the ids
+ for (auto use_to_replace : uses_to_replace) {
+ MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
+ use_to_replace.first, use_to_replace.second));
+ }
+}
+} // namespace fuzz
+} // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
new file mode 100644
index 0000000..4fcc44e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that:
+// - Finds all the zero-like constant definitions in the module and adds the
+// definitions of the corresponding synonym, recording the fact that they
+// are synonymous. If the synonym is already in the module, it does not
+// add a new one.
+// - For each use of a zero-like constant, decides whether to change it to the
+// id of the toggled constant.
+class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
+ public:
+ FuzzerPassInterchangeZeroLikeConstants(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassInterchangeZeroLikeConstants() override;
+
+ void Apply() override;
+
+ private:
+ // Given the declaration of a zero-like constant, it finds or creates the
+ // corresponding toggled constant (a scalar constant of value 0 becomes a
+ // null constant of the same type and vice versa).
+ // Returns the id of the toggled instruction if the constant is zero-like,
+ // 0 otherwise.
+ uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
+
+ // Given an id use (described by an instruction and an index) and an id with
+ // which the original one should be replaced, adds a pair (with the elements
+ // being the corresponding id use descriptor and the replacement id) to
+ // |uses_to_replace| if the use is in an instruction block, otherwise does
+ // nothing.
+ void MaybeAddUseToReplace(
+ opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+ std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+ uses_to_replace);
+};
+
+} // namespace fuzz
+} // namespace spvtools
+#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
new file mode 100644
index 0000000..de4ff1d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_invert_comparison_operator.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() =
+ default;
+
+void FuzzerPassInvertComparisonOperators::Apply() {
+ GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) {
+ if (!TransformationInvertComparisonOperator::IsInversionSupported(
+ inst->opcode())) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) {
+ return;
+ }
+
+ ApplyTransformation(TransformationInvertComparisonOperator(
+ inst->result_id(), GetFuzzerContext()->GetFreshId()));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h
new file mode 100644
index 0000000..9c80bbb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over different comparison operators in the module (>=, <, > etc.)
+// and randomly decides whether to invert each one or not.
+class FuzzerPassInvertComparisonOperators : public FuzzerPass {
+ public:
+ FuzzerPassInvertComparisonOperators(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassInvertComparisonOperators() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
index 49778ae..e66fc44 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
@@ -53,14 +53,9 @@
}
while (!potential_transformations.empty()) {
- uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
- auto transformation = potential_transformations.at(index);
- potential_transformations.erase(potential_transformations.begin() + index);
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
- }
+ auto transformation =
+ GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations);
+ MaybeApplyTransformation(transformation);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index 543c0d7..2775bb8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -311,9 +311,9 @@
} while (constant_index_1 == constant_index_2);
auto constant_id_1 = FindOrCreateConstant(
- available_constant_words[constant_index_1], chosen_type_id);
+ available_constant_words[constant_index_1], chosen_type_id, false);
auto constant_id_2 = FindOrCreateConstant(
- available_constant_words[constant_index_2], chosen_type_id);
+ available_constant_words[constant_index_2], chosen_type_id, false);
assert(constant_id_1 != 0 && constant_id_2 != 0 &&
"We should not find an available constant with an id of 0.");
@@ -361,7 +361,7 @@
// Make sure the module has OpConstant instructions for each index used to
// access a uniform.
for (auto index : uniform_descriptor.index()) {
- FindOrCreate32BitIntegerConstant(index, true);
+ FindOrCreateIntegerConstant({index}, 32, true, false);
}
// Make sure the module has OpTypePointer that points to the element type of
@@ -477,28 +477,18 @@
skipped_opcode_count.clear();
}
- switch (inst.opcode()) {
- case SpvOpPhi:
- // The instruction must not be an OpPhi, as we cannot insert
- // instructions before an OpPhi.
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902):
- // there is scope for being less conservative.
- break;
- case SpvOpVariable:
- // The instruction must not be an OpVariable, the only id that an
- // OpVariable uses is an initializer id, which has to remain
- // constant.
- break;
- default:
- // Consider each operand of the instruction, and add a constant id
- // use for the operand if relevant.
- for (uint32_t in_operand_index = 0;
- in_operand_index < inst.NumInOperands(); in_operand_index++) {
- MaybeAddConstantIdUse(inst, in_operand_index,
- base_instruction_result_id,
- skipped_opcode_count, &constant_uses);
- }
- break;
+ // The instruction must not be an OpVariable, the only id that an
+ // OpVariable uses is an initializer id, which has to remain
+ // constant.
+ if (inst.opcode() != SpvOpVariable) {
+ // Consider each operand of the instruction, and add a constant id
+ // use for the operand if relevant.
+ for (uint32_t in_operand_index = 0;
+ in_operand_index < inst.NumInOperands(); in_operand_index++) {
+ MaybeAddConstantIdUse(inst, in_operand_index,
+ base_instruction_result_id,
+ skipped_opcode_count, &constant_uses);
+ }
}
if (!inst.HasResultId()) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
index 1665d05..1cd291e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_outline_functions.cpp
@@ -17,7 +17,9 @@
#include <vector>
#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_outline_function.h"
+#include "source/fuzz/transformation_split_block.h"
namespace spvtools {
namespace fuzz {
@@ -46,6 +48,31 @@
blocks.push_back(&block);
}
auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+
+ // If the entry block starts with OpPhi, try to split it.
+ if (entry_block->begin()->opcode() == SpvOpPhi) {
+ // Find the first non-OpPhi instruction.
+ opt::Instruction* non_phi_inst;
+ for (auto instruction : *entry_block) {
+ if (instruction.opcode() != SpvOpPhi) {
+ non_phi_inst = &instruction;
+ break;
+ }
+ }
+
+ // If the split was not applicable, the transformation will not work.
+ uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
+ if (!MaybeApplyTransformation(TransformationSplitBlock(
+ MakeInstructionDescriptor(non_phi_inst->result_id(),
+ non_phi_inst->opcode(), 0),
+ new_block_id))) {
+ return;
+ }
+
+ // The new entry block is the newly-created block.
+ entry_block = &*function->FindBlock(new_block_id);
+ }
+
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
auto postdominator_analysis =
GetIRContext()->GetPostDominatorAnalysis(function);
@@ -89,11 +116,7 @@
/*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
/*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
/*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
- }
+ MaybeApplyTransformation(transformation);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
index 27a2d67..24c16fb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
@@ -67,12 +67,7 @@
// down indefinitely.
while (true) {
TransformationMoveBlockDown transformation(*id);
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() =
- transformation.ToMessage();
- } else {
+ if (!MaybeApplyTransformation(transformation)) {
break;
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
index 57d9cab..e15aef6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -61,20 +61,9 @@
std::iota(permutation.begin(), permutation.end(), 0);
GetFuzzerContext()->Shuffle(&permutation);
- // Create a new OpFunctionType instruction with permuted arguments
- // if needed
- auto result_type_id = function_type->GetSingleWordInOperand(0);
- std::vector<uint32_t> argument_ids;
-
- for (auto index : permutation) {
- // +1 to take function's return type into account
- argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
- }
-
// Apply our transformation
ApplyTransformation(TransformationPermuteFunctionParameters(
- function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
- permutation));
+ function_id, GetFuzzerContext()->GetFreshId(), permutation));
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
index 3f32864..bc1031c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -34,7 +34,7 @@
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
- ~FuzzerPassPermuteFunctionParameters();
+ ~FuzzerPassPermuteFunctionParameters() override;
void Apply() override;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
new file mode 100644
index 0000000..c241d9d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
+
+void FuzzerPassPermutePhiOperands::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& /*unused*/) {
+ const auto& inst = *inst_it;
+
+ if (inst.opcode() != SpvOpPhi) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
+ return;
+ }
+
+ // Create a vector of indices for each pair of operands in the |inst|.
+ // OpPhi always has an even number of operands.
+ std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
+ std::iota(permutation.begin(), permutation.end(), 0);
+ GetFuzzerContext()->Shuffle(&permutation);
+
+ ApplyTransformation(TransformationPermutePhiOperands(
+ inst.result_id(), std::move(permutation)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
new file mode 100644
index 0000000..974c2c1
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all instructions in the module and randomly decides for each
+// OpPhi instruction whether to permute its operands.
+class FuzzerPassPermutePhiOperands : public FuzzerPass {
+ public:
+ FuzzerPassPermutePhiOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassPermutePhiOperands() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
new file mode 100644
index 0000000..8d9acaa
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_push_id_through_variable.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassPushIdsThroughVariables::~FuzzerPassPushIdsThroughVariables() =
+ default;
+
+void FuzzerPassPushIdsThroughVariables::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator instruction_iterator,
+ const protobufs::InstructionDescriptor& instruction_descriptor)
+ -> void {
+ assert(instruction_iterator->opcode() ==
+ instruction_descriptor.target_instruction_opcode() &&
+ "The opcode of the instruction we might insert before must be "
+ "the same as the opcode in the descriptor for the instruction");
+
+ // Randomly decide whether to try pushing an id through a variable.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfPushingIdThroughVariable())) {
+ return;
+ }
+
+ // The block containing the instruction we are going to insert before
+ // must be reachable.
+ if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) {
+ return;
+ }
+
+ // It must be valid to insert OpStore and OpLoad instructions
+ // before the instruction to insert before.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpStore, instruction_iterator) ||
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpLoad, instruction_iterator)) {
+ return;
+ }
+
+ // Randomly decides whether a global or local variable will be added.
+ auto variable_storage_class = GetFuzzerContext()->ChooseEven()
+ ? SpvStorageClassPrivate
+ : SpvStorageClassFunction;
+
+ // Gets the available basic and pointer types.
+ auto basic_type_ids_and_pointers =
+ GetAvailableBasicTypesAndPointers(variable_storage_class);
+ auto& basic_types = basic_type_ids_and_pointers.first;
+ uint32_t basic_type_id =
+ basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
+
+ // Looks for ids that we might wish to consider pushing through a
+ // variable.
+ std::vector<opt::Instruction*> value_instructions =
+ FindAvailableInstructions(
+ function, block, instruction_iterator,
+ [this, basic_type_id, instruction_descriptor](
+ opt::IRContext* ir_context,
+ opt::Instruction* instruction) -> bool {
+ if (!instruction->result_id() || !instruction->type_id()) {
+ return false;
+ }
+
+ if (instruction->type_id() != basic_type_id) {
+ return false;
+ }
+
+ // If the id is irrelevant, we can use it since it will not
+ // participate in DataSynonym fact. Otherwise, we should be
+ // able to produce a synonym out of the id.
+ if (!GetTransformationContext()
+ ->GetFactManager()
+ ->IdIsIrrelevant(instruction->result_id()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context,
+ *GetTransformationContext(),
+ instruction)) {
+ return false;
+ }
+
+ return fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context,
+ FindInstruction(instruction_descriptor, ir_context),
+ instruction->result_id());
+ });
+
+ if (value_instructions.empty()) {
+ return;
+ }
+
+ // If the pointer type does not exist, then create it.
+ FindOrCreatePointerType(basic_type_id, variable_storage_class);
+
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+ // type support here is limited by the FindOrCreateZeroConstant
+ // function.
+ const auto* type_inst =
+ GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id);
+ assert(type_inst);
+ switch (type_inst->opcode()) {
+ case SpvOpTypeBool:
+ case SpvOpTypeFloat:
+ case SpvOpTypeInt:
+ case SpvOpTypeArray:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeVector:
+ case SpvOpTypeStruct:
+ break;
+ default:
+ return;
+ }
+
+ // Create a constant to initialize the variable from. This might update
+ // module's id bound so it must be done before any fresh ids are
+ // computed.
+ auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false);
+
+ // Applies the push id through variable transformation.
+ ApplyTransformation(TransformationPushIdThroughVariable(
+ value_instructions[GetFuzzerContext()->RandomIndex(
+ value_instructions)]
+ ->result_id(),
+ GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
+ variable_storage_class, initializer_id, instruction_descriptor));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h
new file mode 100644
index 0000000..3ad5404
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Adds instructions to the module that store existing ids into fresh variables
+// and immediately load from said variables into new ids, thus creating synonyms
+// between the existing and fresh ids.
+class FuzzerPassPushIdsThroughVariables : public FuzzerPass {
+ public:
+ FuzzerPassPushIdsThroughVariables(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassPushIdsThroughVariables() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
new file mode 100644
index 0000000..6847146
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+ FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+ ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
+
+void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // Randomly decide whether to replace the OpCopyMemory.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingCopyMemoryWithLoadStore())) {
+ return;
+ }
+
+ // The instruction must be OpCopyMemory.
+ if (instruction->opcode() != SpvOpCopyMemory) {
+ return;
+ }
+
+ // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore.
+ ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore(
+ GetFuzzerContext()->GetFreshId(),
+ MakeInstructionDescriptor(GetIRContext(), instruction)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
new file mode 100644
index 0000000..2a89006
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyMemory with loading the source variable to
+// an intermediate value and storing this value into the target variable of
+// the original OpCopyMemory instruction.
+class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
+ public:
+ FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
new file mode 100644
index 0000000..e21ea5e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+ FuzzerPassReplaceCopyObjectsWithStoresLoads(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+ ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default;
+
+void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // Randomly decide whether to replace OpCopyObject.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingCopyObjectWithStoreLoad())) {
+ return;
+ }
+ // The instruction must be OpCopyObject.
+ if (instruction->opcode() != SpvOpCopyObject) {
+ return;
+ }
+ // The opcode of the type_id instruction cannot be a OpTypePointer,
+ // because we cannot define a pointer to pointer.
+ if (GetIRContext()
+ ->get_def_use_mgr()
+ ->GetDef(instruction->type_id())
+ ->opcode() == SpvOpTypePointer) {
+ return;
+ }
+ // It must be valid to insert OpStore and OpLoad instructions
+ // before the instruction OpCopyObject.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+ instruction) ||
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) {
+ return;
+ }
+
+ // Randomly decides whether a global or local variable will be added.
+ auto variable_storage_class = GetFuzzerContext()->ChooseEven()
+ ? SpvStorageClassPrivate
+ : SpvStorageClassFunction;
+
+ // Find or create a constant to initialize the variable from.
+ auto variable_initializer_id =
+ FindOrCreateZeroConstant(instruction->type_id(), false);
+
+ // Make sure that pointer type is defined.
+ FindOrCreatePointerType(instruction->type_id(), variable_storage_class);
+ // Apply the transformation replacing OpCopyObject with Store and Load.
+ ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad(
+ instruction->result_id(), GetFuzzerContext()->GetFreshId(),
+ variable_storage_class, variable_initializer_id));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
new file mode 100644
index 0000000..15ec10b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyObject with storing into a new variable
+// and immediately loading this variable to |result_id| of the
+// original OpCopyObject instruction.
+class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass {
+ public:
+ FuzzerPassReplaceCopyObjectsWithStoresLoads(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
new file mode 100644
index 0000000..1e5d697
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceLinearAlgebraInstructions::
+ FuzzerPassReplaceLinearAlgebraInstructions(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceLinearAlgebraInstructions::
+ ~FuzzerPassReplaceLinearAlgebraInstructions() = default;
+
+void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
+ // For each instruction, checks whether it is a supported linear algebra
+ // instruction. In this case, the transformation is randomly applied.
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+ // Right now we only support certain operations. When this issue is
+ // addressed the following conditional can use the function
+ // |spvOpcodeIsLinearAlgebra|.
+ if (instruction->opcode() != SpvOpVectorTimesScalar &&
+ instruction->opcode() != SpvOpMatrixTimesScalar &&
+ instruction->opcode() != SpvOpVectorTimesMatrix &&
+ instruction->opcode() != SpvOpMatrixTimesVector &&
+ instruction->opcode() != SpvOpMatrixTimesMatrix &&
+ instruction->opcode() != SpvOpDot) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingLinearAlgebraInstructions())) {
+ return;
+ }
+
+ ApplyTransformation(TransformationReplaceLinearAlgebraInstruction(
+ GetFuzzerContext()->GetFreshIds(
+ TransformationReplaceLinearAlgebraInstruction::
+ GetRequiredFreshIdCount(GetIRContext(), instruction)),
+ MakeInstructionDescriptor(GetIRContext(), instruction)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
new file mode 100644
index 0000000..2c6126c
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass replaces linear algebra instructions with its mathematical
+// definition.
+class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass {
+ public:
+ FuzzerPassReplaceLinearAlgebraInstructions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceLinearAlgebraInstructions();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
new file mode 100644
index 0000000..7690ac4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
@@ -0,0 +1,105 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+ FuzzerPassReplaceLoadsStoresWithCopyMemories(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+ ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
+
+void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
+ // We look for matching pairs of instructions OpLoad and
+ // OpStore within the same block. Potential instructions OpLoad to be matched
+ // are stored in a hash map. If we encounter instructions that write to memory
+ // or instructions of memory barriers that could operate on variables within
+ // unsafe storage classes we need to erase the hash map to avoid unsafe
+ // operations.
+
+ // A vector of matching OpLoad and OpStore instructions.
+ std::vector<std::pair<opt::Instruction*, opt::Instruction*>>
+ op_load_store_pairs;
+
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ // A hash map storing potential OpLoad instructions.
+ std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
+ for (auto& instruction : block) {
+ // Add a potential OpLoad instruction.
+ if (instruction.opcode() == SpvOpLoad) {
+ current_op_loads[instruction.result_id()] = &instruction;
+ } else if (instruction.opcode() == SpvOpStore) {
+ if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
+ current_op_loads.end()) {
+ // We have found the matching OpLoad instruction to the current
+ // OpStore instruction.
+ op_load_store_pairs.push_back(std::make_pair(
+ current_op_loads[instruction.GetSingleWordOperand(1)],
+ &instruction));
+ }
+ }
+ if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+ instruction.opcode())) {
+ current_op_loads.clear();
+ } else if (TransformationReplaceLoadStoreWithCopyMemory::
+ IsMemoryBarrierOpCode(instruction.opcode())) {
+ for (auto it = current_op_loads.begin();
+ it != current_op_loads.end();) {
+ // Get the storage class.
+ opt::Instruction* source_id =
+ GetIRContext()->get_def_use_mgr()->GetDef(
+ it->second->GetSingleWordOperand(2));
+ SpvStorageClass storage_class =
+ fuzzerutil::GetStorageClassFromPointerType(
+ GetIRContext(), source_id->type_id());
+ if (!TransformationReplaceLoadStoreWithCopyMemory::
+ IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+ it = current_op_loads.erase(it);
+ } else {
+ it++;
+ }
+ }
+ }
+ }
+ }
+ }
+ for (auto instr_pair : op_load_store_pairs) {
+ // Randomly decide to apply the transformation for the
+ // potential pairs.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfReplacingLoadStoreWithCopyMemory())) {
+ ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory(
+ MakeInstructionDescriptor(GetIRContext(), instr_pair.first),
+ MakeInstructionDescriptor(GetIRContext(), instr_pair.second)));
+ }
+ }
+} // namespace fuzz
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
new file mode 100644
index 0000000..db09500
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and
+// OpStore that have the same intermediate value and in each pair replaces the
+// OpStore with an equivalent OpCopyMemory.
+class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
+ public:
+ FuzzerPassReplaceLoadsStoresWithCopyMemories(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
new file mode 100644
index 0000000..8672a3b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
@@ -0,0 +1,93 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() =
+ default;
+
+void FuzzerPassReplaceParameterWithGlobal::Apply() {
+ for (const auto& function : *GetIRContext()->module()) {
+ if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+ function.result_id())) {
+ continue;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) {
+ continue;
+ }
+
+ auto params =
+ fuzzerutil::GetParameters(GetIRContext(), function.result_id());
+
+ // Make sure at least one parameter can be replaced. Also checks that the
+ // function has at least one parameter.
+ if (std::none_of(params.begin(), params.end(),
+ [this](const opt::Instruction* param) {
+ const auto* param_type =
+ GetIRContext()->get_type_mgr()->GetType(
+ param->type_id());
+ assert(param_type && "Parameter has invalid type");
+ return TransformationReplaceParameterWithGlobal::
+ IsParameterTypeSupported(*param_type);
+ })) {
+ continue;
+ }
+
+ // Select id of a parameter to replace.
+ const opt::Instruction* replaced_param = nullptr;
+ const opt::analysis::Type* param_type = nullptr;
+ do {
+ replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(¶ms);
+ param_type =
+ GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
+ assert(param_type && "Parameter has invalid type");
+ } while (
+ !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
+ *param_type));
+
+ assert(replaced_param && "Unable to find a parameter to replace");
+
+ // Make sure type id for the global variable exists in the module.
+ FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
+
+ // Make sure initializer for the global variable exists in the module.
+ FindOrCreateZeroConstant(replaced_param->type_id(), false);
+
+ ApplyTransformation(TransformationReplaceParameterWithGlobal(
+ GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),
+ GetFuzzerContext()->GetFreshId()));
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
new file mode 100644
index 0000000..25011bd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all non-entry-point functions in the module and randomly
+// replaces a parameter with a global variable.
+class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass {
+ public:
+ FuzzerPassReplaceParameterWithGlobal(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceParameterWithGlobal() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
new file mode 100644
index 0000000..e49eacb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
+ default;
+
+void FuzzerPassReplaceParamsWithStruct::Apply() {
+ for (const auto& function : *GetIRContext()->module()) {
+ auto params =
+ fuzzerutil::GetParameters(GetIRContext(), function.result_id());
+
+ if (params.empty() || fuzzerutil::FunctionIsEntryPoint(
+ GetIRContext(), function.result_id())) {
+ continue;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) {
+ continue;
+ }
+
+ std::vector<uint32_t> parameter_index(params.size());
+ std::iota(parameter_index.begin(), parameter_index.end(), 0);
+
+ // Remove the indices of unsupported parameters.
+ auto new_end = std::remove_if(
+ parameter_index.begin(), parameter_index.end(),
+ [this, ¶ms](uint32_t index) {
+ const auto* type =
+ GetIRContext()->get_type_mgr()->GetType(params[index]->type_id());
+ assert(type && "Parameter has invalid type");
+ return !TransformationReplaceParamsWithStruct::
+ IsParameterTypeSupported(*type);
+ });
+
+ // std::remove_if changes the vector so that removed elements are placed at
+ // the end (i.e. [new_end, parameter_index.end()) is a range of removed
+ // elements). However, the size of the vector is not changed so we need to
+ // change that explicitly by popping those elements from the vector.
+ parameter_index.erase(new_end, parameter_index.end());
+
+ if (parameter_index.empty()) {
+ continue;
+ }
+
+ // Select |num_replaced_params| parameters at random. We shuffle the vector
+ // of indices for randomization and shrink it to select first
+ // |num_replaced_params| parameters.
+ auto num_replaced_params = std::min<size_t>(
+ parameter_index.size(),
+ GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct(
+ static_cast<uint32_t>(params.size())));
+
+ GetFuzzerContext()->Shuffle(¶meter_index);
+ parameter_index.resize(num_replaced_params);
+
+ // Make sure OpTypeStruct exists in the module.
+ std::vector<uint32_t> component_type_ids;
+ for (auto index : parameter_index) {
+ component_type_ids.push_back(params[index]->type_id());
+ }
+
+ FindOrCreateStructType(component_type_ids);
+
+ // Map parameters' indices to parameters' ids.
+ std::vector<uint32_t> parameter_id;
+ for (auto index : parameter_index) {
+ parameter_id.push_back(params[index]->result_id());
+ }
+
+ std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
+ for (const auto* inst :
+ fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
+ caller_id_to_fresh_id[inst->result_id()] =
+ GetFuzzerContext()->GetFreshId();
+ }
+
+ ApplyTransformation(TransformationReplaceParamsWithStruct(
+ parameter_id, GetFuzzerContext()->GetFreshId(),
+ GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id));
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
new file mode 100644
index 0000000..ed1aa6b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all functions in the module and randomly decides for each one
+// whether to replace a subset of its parameters with a struct value.
+class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
+ public:
+ FuzzerPassReplaceParamsWithStruct(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassReplaceParamsWithStruct() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
index 15c6790..481cd96 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_split_blocks.cpp
@@ -96,11 +96,7 @@
// If the position we have chosen turns out to be a valid place to split
// the block, we apply the split. Otherwise the block just doesn't get
// split.
- if (transformation.IsApplicable(GetIRContext(),
- *GetTransformationContext())) {
- transformation.Apply(GetIRContext(), GetTransformationContext());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
- }
+ MaybeApplyTransformation(transformation);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..dc8b1eb
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapBranchConditionalOperands::
+ FuzzerPassSwapBranchConditionalOperands(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassSwapBranchConditionalOperands::
+ ~FuzzerPassSwapBranchConditionalOperands() = default;
+
+void FuzzerPassSwapBranchConditionalOperands::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ const auto& inst = *inst_it;
+
+ if (inst.opcode() != SpvOpBranchConditional) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfSwappingConditionalBranchOperands())) {
+ return;
+ }
+
+ ApplyTransformation(TransformationSwapConditionalBranchOperands(
+ instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..f84f3ba
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each OpBranchConditional instruction in the module
+// whether to swap its operands or not.
+class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
+ public:
+ FuzzerPassSwapBranchConditionalOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassSwapBranchConditionalOperands() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index f09943f..16cbf1a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -14,12 +14,34 @@
#include "source/fuzz/fuzzer_util.h"
+#include <algorithm>
+#include <unordered_set>
+
#include "source/opt/build_module.h"
namespace spvtools {
namespace fuzz {
namespace fuzzerutil {
+namespace {
+
+uint32_t MaybeGetOpConstant(opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words,
+ uint32_t type_id, bool is_irrelevant) {
+ for (const auto& inst : ir_context->types_values()) {
+ if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
+ inst.GetInOperand(0).words == words &&
+ transformation_context.GetFactManager()->IdIsIrrelevant(
+ inst.result_id()) == is_irrelevant) {
+ return inst.result_id();
+ }
+ }
+
+ return 0;
+}
+
+} // namespace
bool IsFreshId(opt::IRContext* context, uint32_t id) {
return !context->get_def_use_mgr()->GetDef(id);
@@ -109,22 +131,9 @@
return true;
}
-uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
- opt::analysis::Bool bool_type;
- auto registered_bool_type =
- context->get_type_mgr()->GetRegisteredType(&bool_type);
- if (!registered_bool_type) {
- return 0;
- }
- opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
- value);
- return context->get_constant_mgr()->FindDeclaredConstant(
- &bool_constant, context->get_type_mgr()->GetId(&bool_type));
-}
-
void AddUnreachableEdgeAndUpdateOpPhis(
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
- bool condition_value,
+ uint32_t bool_id,
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
"Precondition on phi_ids is not satisfied");
@@ -132,10 +141,13 @@
"Precondition on terminator of bb_from is not satisfied");
// Get the id of the boolean constant to be used as the condition.
- uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
- assert(
- bool_id &&
- "Precondition that condition value must be available is not satisfied");
+ auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id);
+ assert(condition_inst &&
+ (condition_inst->opcode() == SpvOpConstantTrue ||
+ condition_inst->opcode() == SpvOpConstantFalse) &&
+ "|bool_id| is invalid");
+
+ auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -166,6 +178,25 @@
}
}
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+ uint32_t loop_header_id) {
+ auto block = context->cfg()->block(block_id);
+ auto loop_header = context->cfg()->block(loop_header_id);
+
+ // |block| and |loop_header| must be defined, |loop_header| must be in fact
+ // loop header and |block| must branch to it.
+ if (!(block && loop_header && loop_header->IsLoopHeader() &&
+ block->IsSuccessor(loop_header))) {
+ return false;
+ }
+
+ // |block_id| must be reachable and be dominated by |loop_header|.
+ opt::DominatorAnalysis* dominator_analysis =
+ context->GetDominatorAnalysis(loop_header->GetParent());
+ return dominator_analysis->IsReachable(block_id) &&
+ dominator_analysis->Dominates(loop_header_id, block_id);
+}
+
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
uint32_t maybe_loop_header_id) {
// We deem a block to be part of a loop's continue construct if the loop's
@@ -217,7 +248,9 @@
return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
}
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ opt::Instruction* inst) {
if (inst->opcode() == SpvOpSampledImage) {
// The SPIR-V data rules say that only very specific instructions may
// may consume the result id of an OpSampledImage, and this excludes the
@@ -228,6 +261,11 @@
// We can only make a synonym of an instruction that generates an id.
return false;
}
+ if (transformation_context.GetFactManager()->IdIsIrrelevant(
+ inst->result_id())) {
+ // An irrelevant id can't be a synonym of anything.
+ return false;
+ }
if (!inst->type_id()) {
// We can only make a synonym of an instruction that has a type.
return false;
@@ -335,6 +373,28 @@
return array_length_constant->GetU32();
}
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+ opt::IRContext* ir_context) {
+ switch (composite_type_inst.opcode()) {
+ case SpvOpTypeArray:
+ return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
+ case SpvOpTypeMatrix:
+ case SpvOpTypeVector:
+ return composite_type_inst.GetSingleWordInOperand(1);
+ case SpvOpTypeStruct: {
+ return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
+ }
+ case SpvOpTypeRuntimeArray:
+ assert(false &&
+ "GetBoundForCompositeIndex should not be invoked with an "
+ "OpTypeRuntimeArray, which does not have a static bound.");
+ return 0;
+ default:
+ assert(false && "Unknown composite type.");
+ return 0;
+ }
+}
+
bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
@@ -543,6 +603,12 @@
return 0;
}
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+ uint32_t absolute_index) {
+ // Subtract the number of non-input operands from the index
+ return absolute_index - inst.NumOperands() + inst.NumInOperands();
+}
+
bool IsNullConstantSupported(const opt::analysis::Type& type) {
return type.AsBool() || type.AsInteger() || type.AsFloat() ||
type.AsMatrix() || type.AsVector() || type.AsArray() ||
@@ -550,6 +616,663 @@
type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
}
+bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
+ const opt::IRContext* ir_context) {
+ // TODO(afd): We capture the universal environments for which this requirement
+ // holds. The check should be refined on demand for other target
+ // environments.
+ switch (ir_context->grammar().target_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:
+ return false;
+ default:
+ return true;
+ }
+}
+
+void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
+ if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) {
+ // Conservatively add this global to the interface of every entry point in
+ // the module. This means that the global is available for other
+ // transformations to use.
+ //
+ // A downside of this is that the global will be in the interface even if it
+ // ends up never being used.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
+ // this if a more thorough approach to entry point interfaces is taken.
+ for (auto& entry_point : context->module()->entry_points()) {
+ entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}});
+ }
+ }
+}
+
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+ uint32_t type_id, SpvStorageClass storage_class,
+ uint32_t initializer_id) {
+ // Check various preconditions.
+ assert(result_id != 0 && "Result id can't be 0");
+
+ assert((storage_class == SpvStorageClassPrivate ||
+ storage_class == SpvStorageClassWorkgroup) &&
+ "Variable's storage class must be either Private or Workgroup");
+
+ auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+ (void)type_inst; // Variable becomes unused in release mode.
+ assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+ GetStorageClassFromPointerType(type_inst) == storage_class &&
+ "Variable's type is invalid");
+
+ if (storage_class == SpvStorageClassWorkgroup) {
+ assert(initializer_id == 0);
+ }
+
+ if (initializer_id != 0) {
+ const auto* constant_inst =
+ context->get_def_use_mgr()->GetDef(initializer_id);
+ (void)constant_inst; // Variable becomes unused in release mode.
+ assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+ GetPointeeTypeIdFromPointerType(type_inst) ==
+ constant_inst->type_id() &&
+ "Initializer is invalid");
+ }
+
+ opt::Instruction::OperandList operands = {
+ {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
+
+ if (initializer_id) {
+ operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
+ }
+
+ context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+ context, SpvOpVariable, type_id, result_id, std::move(operands)));
+
+ AddVariableIdToEntryPointInterfaces(context, result_id);
+ UpdateModuleIdBound(context, result_id);
+}
+
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+ uint32_t type_id, uint32_t function_id,
+ uint32_t initializer_id) {
+ // Check various preconditions.
+ assert(result_id != 0 && "Result id can't be 0");
+
+ auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+ (void)type_inst; // Variable becomes unused in release mode.
+ assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+ GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
+ "Variable's type is invalid");
+
+ const auto* constant_inst =
+ context->get_def_use_mgr()->GetDef(initializer_id);
+ (void)constant_inst; // Variable becomes unused in release mode.
+ assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+ GetPointeeTypeIdFromPointerType(type_inst) ==
+ constant_inst->type_id() &&
+ "Initializer is invalid");
+
+ auto* function = FindFunction(context, function_id);
+ assert(function && "Function id is invalid");
+
+ function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+ context, SpvOpVariable, type_id, result_id,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
+ {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
+
+ UpdateModuleIdBound(context, result_id);
+}
+
+bool HasDuplicates(const std::vector<uint32_t>& arr) {
+ return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
+ arr.size();
+}
+
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+ uint32_t hi) {
+ if (arr.empty()) {
+ return lo > hi;
+ }
+
+ if (HasDuplicates(arr)) {
+ return false;
+ }
+
+ auto min_max = std::minmax_element(arr.begin(), arr.end());
+ return arr.size() == hi - lo + 1 && *min_max.first == lo &&
+ *min_max.second == hi;
+}
+
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+ uint32_t function_id) {
+ auto* function = FindFunction(ir_context, function_id);
+ assert(function && "|function_id| is invalid");
+
+ std::vector<opt::Instruction*> result;
+ function->ForEachParam(
+ [&result](opt::Instruction* inst) { result.push_back(inst); });
+
+ return result;
+}
+
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+ uint32_t function_id) {
+ assert(FindFunction(ir_context, function_id) &&
+ "|function_id| is not a result id of a function");
+
+ std::vector<opt::Instruction*> result;
+ ir_context->get_def_use_mgr()->ForEachUser(
+ function_id, [&result, function_id](opt::Instruction* inst) {
+ if (inst->opcode() == SpvOpFunctionCall &&
+ inst->GetSingleWordInOperand(0) == function_id) {
+ result.push_back(inst);
+ }
+ });
+
+ return result;
+}
+
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+ uint32_t param_id) {
+ auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
+ assert(param_inst && "Parameter id is invalid");
+
+ for (auto& function : *ir_context->module()) {
+ if (InstructionIsFunctionParameter(param_inst, &function)) {
+ return &function;
+ }
+ }
+
+ return nullptr;
+}
+
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+ uint32_t new_function_type_result_id,
+ uint32_t return_type_id,
+ const std::vector<uint32_t>& parameter_type_ids) {
+ // Check some initial constraints.
+ assert(ir_context->get_type_mgr()->GetType(return_type_id) &&
+ "Return type is invalid");
+ for (auto id : parameter_type_ids) {
+ const auto* type = ir_context->get_type_mgr()->GetType(id);
+ (void)type; // Make compilers happy in release mode.
+ // Parameters can't be OpTypeVoid.
+ assert(type && !type->AsVoid() && "Parameter has invalid type");
+ }
+
+ auto* function = FindFunction(ir_context, function_id);
+ assert(function && "|function_id| is invalid");
+
+ auto* old_function_type = GetFunctionType(ir_context, function);
+ assert(old_function_type && "Function has invalid type");
+
+ std::vector<uint32_t> operand_ids = {return_type_id};
+ operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
+ parameter_type_ids.end());
+
+ if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
+ FindFunctionType(ir_context, operand_ids) == 0) {
+ // We can change |old_function_type| only if it's used once in the module
+ // and we are certain we won't create a duplicate as a result of the change.
+
+ // Update |old_function_type| in-place.
+ opt::Instruction::OperandList operands;
+ for (auto id : operand_ids) {
+ operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+ }
+
+ old_function_type->SetInOperands(std::move(operands));
+
+ // |operands| may depend on result ids defined below the |old_function_type|
+ // in the module.
+ old_function_type->RemoveFromList();
+ ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
+ return old_function_type->result_id();
+ } else {
+ // We can't modify the |old_function_type| so we have to either use an
+ // existing one or create a new one.
+ auto type_id = FindOrCreateFunctionType(
+ ir_context, new_function_type_result_id, operand_ids);
+
+ if (type_id != old_function_type->result_id()) {
+ function->DefInst().SetInOperand(1, {type_id});
+
+ // DefUseManager hasn't been updated yet, so if the following condition is
+ // true, then |old_function_type| will have no users when this function
+ // returns. We might as well remove it.
+ if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+ old_function_type->RemoveFromList();
+ delete old_function_type;
+ }
+ }
+
+ return type_id;
+ }
+}
+
+void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
+ const std::vector<uint32_t>& type_ids) {
+ assert(result_id != 0 && "Result id can't be 0");
+ assert(!type_ids.empty() &&
+ "OpTypeFunction always has at least one operand - function's return "
+ "type");
+ assert(IsNonFunctionTypeId(ir_context, type_ids[0]) &&
+ "Return type must not be a function");
+
+ for (size_t i = 1; i < type_ids.size(); ++i) {
+ const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]);
+ (void)param_type; // Make compiler happy in release mode.
+ assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() &&
+ "Function parameter can't have a function or void type");
+ }
+
+ opt::Instruction::OperandList operands;
+ operands.reserve(type_ids.size());
+ for (auto id : type_ids) {
+ operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+ }
+
+ ir_context->AddType(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands)));
+
+ UpdateModuleIdBound(ir_context, result_id);
+}
+
+uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
+ uint32_t result_id,
+ const std::vector<uint32_t>& type_ids) {
+ if (auto existing_id = FindFunctionType(ir_context, type_ids)) {
+ return existing_id;
+ }
+ AddFunctionType(ir_context, result_id, type_ids);
+ return result_id;
+}
+
+uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
+ bool is_signed) {
+ opt::analysis::Integer type(width, is_signed);
+ return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) {
+ opt::analysis::Float type(width);
+ return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context) {
+ opt::analysis::Bool type;
+ return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
+ uint32_t component_type_id,
+ uint32_t element_count) {
+ const auto* component_type =
+ ir_context->get_type_mgr()->GetType(component_type_id);
+ assert(component_type &&
+ (component_type->AsInteger() || component_type->AsFloat() ||
+ component_type->AsBool()) &&
+ "|component_type_id| is invalid");
+ assert(element_count >= 2 && element_count <= 4 &&
+ "Precondition: component count must be in range [2, 4].");
+ opt::analysis::Vector type(component_type, element_count);
+ return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetStructType(opt::IRContext* ir_context,
+ const std::vector<uint32_t>& component_type_ids) {
+ std::vector<const opt::analysis::Type*> component_types;
+ component_types.reserve(component_type_ids.size());
+
+ for (auto type_id : component_type_ids) {
+ const auto* component_type = ir_context->get_type_mgr()->GetType(type_id);
+ assert(component_type && !component_type->AsFunction() &&
+ "Component type is invalid");
+ component_types.push_back(component_type);
+ }
+
+ opt::analysis::Struct type(component_types);
+ return ir_context->get_type_mgr()->GetId(&type);
+}
+
+uint32_t MaybeGetZeroConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
+ const auto* type =
+ ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
+ assert(type && "|scalar_or_composite_type_id| is invalid");
+
+ switch (type->kind()) {
+ case opt::analysis::Type::kBool:
+ return MaybeGetBoolConstant(ir_context, transformation_context, false,
+ is_irrelevant);
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kInteger: {
+ std::vector<uint32_t> words = {0};
+ if ((type->AsInteger() && type->AsInteger()->width() > 32) ||
+ (type->AsFloat() && type->AsFloat()->width() > 32)) {
+ words.push_back(0);
+ }
+
+ return MaybeGetScalarConstant(ir_context, transformation_context, words,
+ scalar_or_composite_type_id, is_irrelevant);
+ }
+ case opt::analysis::Type::kStruct: {
+ std::vector<uint32_t> component_ids;
+ for (const auto* component_type : type->AsStruct()->element_types()) {
+ auto component_type_id =
+ ir_context->get_type_mgr()->GetId(component_type);
+ assert(component_type_id && "Component type is invalid");
+
+ auto component_id =
+ MaybeGetZeroConstant(ir_context, transformation_context,
+ component_type_id, is_irrelevant);
+ if (component_id == 0 && is_irrelevant) {
+ // Irrelevant constants can use either relevant or irrelevant
+ // constituents.
+ component_id = MaybeGetZeroConstant(
+ ir_context, transformation_context, component_type_id, false);
+ }
+
+ if (component_id == 0) {
+ return 0;
+ }
+
+ component_ids.push_back(component_id);
+ }
+
+ return MaybeGetCompositeConstant(
+ ir_context, transformation_context, component_ids,
+ scalar_or_composite_type_id, is_irrelevant);
+ }
+ case opt::analysis::Type::kMatrix:
+ case opt::analysis::Type::kVector: {
+ const auto* component_type = type->AsVector()
+ ? type->AsVector()->element_type()
+ : type->AsMatrix()->element_type();
+ auto component_type_id =
+ ir_context->get_type_mgr()->GetId(component_type);
+ assert(component_type_id && "Component type is invalid");
+
+ auto component_id = MaybeGetZeroConstant(
+ ir_context, transformation_context, component_type_id, is_irrelevant);
+
+ if (component_id == 0 && is_irrelevant) {
+ // Irrelevant constants can use either relevant or irrelevant
+ // constituents.
+ component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+ component_type_id, false);
+ }
+
+ if (component_id == 0) {
+ return 0;
+ }
+
+ auto component_count = type->AsVector()
+ ? type->AsVector()->element_count()
+ : type->AsMatrix()->element_count();
+ return MaybeGetCompositeConstant(
+ ir_context, transformation_context,
+ std::vector<uint32_t>(component_count, component_id),
+ scalar_or_composite_type_id, is_irrelevant);
+ }
+ case opt::analysis::Type::kArray: {
+ auto component_type_id =
+ ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
+ assert(component_type_id && "Component type is invalid");
+
+ auto component_id = MaybeGetZeroConstant(
+ ir_context, transformation_context, component_type_id, is_irrelevant);
+
+ if (component_id == 0 && is_irrelevant) {
+ component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+ component_type_id, false);
+ }
+
+ if (component_id == 0) {
+ return 0;
+ }
+
+ auto type_id = ir_context->get_type_mgr()->GetId(type);
+ assert(type_id && "|type| is invalid");
+
+ const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
+ assert(type_inst && "Array's type id is invalid");
+
+ return MaybeGetCompositeConstant(
+ ir_context, transformation_context,
+ std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
+ component_id),
+ scalar_or_composite_type_id, is_irrelevant);
+ }
+ default:
+ assert(false && "Type is not supported");
+ return 0;
+ }
+}
+
+uint32_t MaybeGetScalarConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+ bool is_irrelevant) {
+ const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
+ assert(type && "|scalar_type_id| is invalid");
+
+ if (const auto* int_type = type->AsInteger()) {
+ return MaybeGetIntegerConstant(ir_context, transformation_context, words,
+ int_type->width(), int_type->IsSigned(),
+ is_irrelevant);
+ } else if (const auto* float_type = type->AsFloat()) {
+ return MaybeGetFloatConstant(ir_context, transformation_context, words,
+ float_type->width(), is_irrelevant);
+ } else {
+ assert(type->AsBool() && words.size() == 1 &&
+ "|scalar_type_id| doesn't represent a scalar type");
+ return MaybeGetBoolConstant(ir_context, transformation_context, words[0],
+ is_irrelevant);
+ }
+}
+
+uint32_t MaybeGetCompositeConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+ bool is_irrelevant) {
+ const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
+ (void)type; // Make compilers happy in release mode.
+ assert(type &&
+ (type->AsArray() || type->AsStruct() || type->AsVector() ||
+ type->AsMatrix()) &&
+ "|composite_type_id| is invalid");
+
+ for (const auto& inst : ir_context->types_values()) {
+ if (inst.opcode() == SpvOpConstantComposite &&
+ inst.type_id() == composite_type_id &&
+ transformation_context.GetFactManager()->IdIsIrrelevant(
+ inst.result_id()) == is_irrelevant &&
+ inst.NumInOperands() == component_ids.size()) {
+ bool is_match = true;
+
+ for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
+ if (inst.GetSingleWordInOperand(i) != component_ids[i]) {
+ is_match = false;
+ break;
+ }
+ }
+
+ if (is_match) {
+ return inst.result_id();
+ }
+ }
+ }
+
+ return 0;
+}
+
+uint32_t MaybeGetIntegerConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+ bool is_irrelevant) {
+ if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
+ return MaybeGetOpConstant(ir_context, transformation_context, words,
+ type_id, is_irrelevant);
+ }
+
+ return 0;
+}
+
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+ uint32_t value,
+ uint32_t int_type_id) {
+ auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id);
+
+ assert(int_type_inst && "The given type id must exist.");
+
+ auto int_type = ir_context->get_type_mgr()
+ ->GetType(int_type_inst->result_id())
+ ->AsInteger();
+
+ assert(int_type && int_type->width() == 32 &&
+ "The given type id must correspond to an 32-bit integer type.");
+
+ opt::analysis::IntConstant constant(int_type, {value});
+
+ // Check that the constant exists in the module.
+ if (!ir_context->get_constant_mgr()->FindConstant(&constant)) {
+ return 0;
+ }
+
+ return ir_context->get_constant_mgr()
+ ->GetDefiningInstruction(&constant)
+ ->result_id();
+}
+
+uint32_t MaybeGetFloatConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
+ if (auto type_id = MaybeGetFloatType(ir_context, width)) {
+ return MaybeGetOpConstant(ir_context, transformation_context, words,
+ type_id, is_irrelevant);
+ }
+
+ return 0;
+}
+
+uint32_t MaybeGetBoolConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context, bool value,
+ bool is_irrelevant) {
+ if (auto type_id = MaybeGetBoolType(ir_context)) {
+ for (const auto& inst : ir_context->types_values()) {
+ if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
+ inst.type_id() == type_id &&
+ transformation_context.GetFactManager()->IdIsIrrelevant(
+ inst.result_id()) == is_irrelevant) {
+ return inst.result_id();
+ }
+ }
+ }
+
+ return 0;
+}
+
+void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t width, bool is_signed) {
+ ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpTypeInt, 0, result_id,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}}));
+
+ UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t width) {
+ ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpTypeFloat, 0, result_id,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}}));
+
+ UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t component_type_id, uint32_t element_count) {
+ const auto* component_type =
+ ir_context->get_type_mgr()->GetType(component_type_id);
+ (void)component_type; // Make compiler happy in release mode.
+ assert(component_type &&
+ (component_type->AsInteger() || component_type->AsFloat() ||
+ component_type->AsBool()) &&
+ "|component_type_id| is invalid");
+ assert(element_count >= 2 && element_count <= 4 &&
+ "Precondition: component count must be in range [2, 4].");
+ ir_context->module()->AddType(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpTypeVector, 0, result_id,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {component_type_id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}}));
+
+ UpdateModuleIdBound(ir_context, result_id);
+}
+
+void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
+ const std::vector<uint32_t>& component_type_ids) {
+ opt::Instruction::OperandList operands;
+ operands.reserve(component_type_ids.size());
+
+ for (auto type_id : component_type_ids) {
+ const auto* type = ir_context->get_type_mgr()->GetType(type_id);
+ (void)type; // Make compiler happy in release mode.
+ assert(type && !type->AsFunction() && "Component's type id is invalid");
+ operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
+ }
+
+ ir_context->AddType(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands)));
+
+ UpdateModuleIdBound(ir_context, result_id);
+}
+
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+ uint32_t type2_id) {
+ if (type1_id == type2_id) {
+ return true;
+ }
+
+ auto type1 = ir_context->get_type_mgr()->GetType(type1_id);
+ auto type2 = ir_context->get_type_mgr()->GetType(type2_id);
+
+ // Integer scalar types must have the same width
+ if (type1->AsInteger() && type2->AsInteger()) {
+ return type1->AsInteger()->width() == type2->AsInteger()->width();
+ }
+
+ // Integer vector types must have the same number of components and their
+ // component types must be integers with the same width.
+ if (type1->AsVector() && type2->AsVector()) {
+ auto component_type1 = type1->AsVector()->element_type()->AsInteger();
+ auto component_type2 = type2->AsVector()->element_type()->AsInteger();
+
+ // Only check the component count and width if they are integer.
+ if (component_type1 && component_type2) {
+ return type1->AsVector()->element_count() ==
+ type2->AsVector()->element_count() &&
+ component_type1->width() == component_type2->width();
+ }
+ }
+
+ // In all other cases, the types cannot be considered equal.
+ return false;
+}
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
index bccd1d0..af8ff16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -53,21 +53,25 @@
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
-// Returns the id of a boolean constant with value |value| if it exists in the
-// module, or 0 otherwise.
-uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value);
-
-// Requires that a boolean constant with value |condition_value| is available,
-// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
-// bb_from ends with "OpBranch %some_block". Turns OpBranch into
-// "OpBranchConditional |condition_value| ...", such that control will branch
-// to %some_block, with |bb_to| being the unreachable alternative. Updates
-// OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
+// Requires that |bool_id| is a valid result id of either OpConstantTrue or
+// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
+// holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch
+// into "OpBranchConditional |condition_value| ...", such that control will
+// branch to %some_block, with |bb_to| being the unreachable alternative.
+// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is
+// valid. |condition_value| above is equal to |true| if |bool_id| is a result id
+// of an OpConstantTrue instruction.
void AddUnreachableEdgeAndUpdateOpPhis(
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
- bool condition_value,
+ uint32_t bool_id,
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
+// Returns true if and only if |loop_header_id| is a loop header and
+// |block_id| is a reachable block branching to and dominated by
+// |loop_header_id|.
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+ uint32_t loop_header_id);
+
// Returns true if and only if |maybe_loop_header_id| is a loop header and
// |block_id| is in the continue construct of the associated loop.
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
@@ -89,7 +93,11 @@
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
// Determines whether it is OK to make a synonym of |inst|.
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
+// |transformation_context| is used to verify that the result id of |inst|
+// does not participate in IdIsIrrelevant fact.
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ opt::Instruction* inst);
// Determines whether the given type is a composite; that is: an array, matrix,
// struct or vector.
@@ -133,6 +141,13 @@
uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
opt::IRContext* context);
+// Returns the bound for indexing into a composite of type
+// |composite_type_inst|, i.e. the number of fields of a struct, the size of an
+// array, the number of components of a vector, or the number of columns of a
+// matrix. |composite_type_inst| must be the type of a composite.
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+ opt::IRContext* ir_context);
+
// Returns true if and only if |context| is valid, according to the validator
// instantiated with |validator_options|.
bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
@@ -211,10 +226,243 @@
uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
SpvStorageClass storage_class);
+// Given an instruction |inst| and an operand absolute index |absolute_index|,
+// returns the index of the operand restricted to the input operands.
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+ uint32_t absolute_index);
+
// Returns true if and only if |type| is one of the types for which it is legal
// to have an OpConstantNull value.
bool IsNullConstantSupported(const opt::analysis::Type& type);
+// Returns true if and only if the SPIR-V version being used requires that
+// global variables accessed in the static call graph of an entry point need
+// to be listed in that entry point's interface.
+bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
+ const opt::IRContext* context);
+
+// Adds |id| into the interface of every entry point of the shader.
+// Does nothing if SPIR-V doesn't require global variables, that are accessed
+// from an entry point function, to be listed in that function's interface.
+void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
+
+// Adds a global variable with storage class |storage_class| to the module, with
+// type |type_id| and either no initializer or |initializer_id| as an
+// initializer, depending on whether |initializer_id| is 0. The global variable
+// has result id |result_id|. Updates module's id bound to accommodate for
+// |result_id|.
+//
+// - |type_id| must be the id of a pointer type with the same storage class as
+// |storage_class|.
+// - |storage_class| must be Private or Workgroup.
+// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
+// may either be 0 or the id of a constant whose type is the pointee type of
+// |type_id|.
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+ uint32_t type_id, SpvStorageClass storage_class,
+ uint32_t initializer_id);
+
+// Adds an instruction to the start of |function_id|, of the form:
+// |result_id| = OpVariable |type_id| Function |initializer_id|.
+// Updates module's id bound to accommodate for |result_id|.
+//
+// - |type_id| must be the id of a pointer type with Function storage class.
+// - |initializer_id| must be the id of a constant with the same type as the
+// pointer's pointee type.
+// - |function_id| must be the id of a function.
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+ uint32_t type_id, uint32_t function_id,
+ uint32_t initializer_id);
+
+// Returns true if the vector |arr| has duplicates.
+bool HasDuplicates(const std::vector<uint32_t>& arr);
+
+// Checks that the given vector |arr| contains a permutation of a range
+// [lo, hi]. That being said, all elements in the range are present without
+// duplicates. If |arr| is empty, returns true iff |lo > hi|.
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+ uint32_t hi);
+
+// Returns OpFunctionParameter instructions corresponding to the function
+// with result id |function_id|.
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+ uint32_t function_id);
+
+// Returns all OpFunctionCall instructions that call a function with result id
+// |function_id|.
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+ uint32_t function_id);
+
+// Returns a function that contains OpFunctionParameter instruction with result
+// id |param_id|. Returns nullptr if the module has no such function.
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+ uint32_t param_id);
+
+// Changes the type of function |function_id| so that its return type is
+// |return_type_id| and its parameters' types are |parameter_type_ids|. If a
+// suitable function type already exists in the module, it is used, otherwise
+// |new_function_type_result_id| is used as the result id of a suitable new
+// function type instruction. If the old type of the function doesn't have any
+// more users, it is removed from the module. Returns the result id of the
+// OpTypeFunction instruction that is used as a type of the function with
+// |function_id|.
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+ uint32_t new_function_type_result_id,
+ uint32_t return_type_id,
+ const std::vector<uint32_t>& parameter_type_ids);
+
+// Creates new OpTypeFunction instruction in the module. |type_ids| may not be
+// empty. It may not contain result ids of OpTypeFunction instructions.
+// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
+// |result_id| may not equal to 0. Updates module's id bound to accommodate for
+// |result_id|.
+void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
+ const std::vector<uint32_t>& type_ids);
+
+// Returns a result id of an OpTypeFunction instruction in the module. Creates a
+// new instruction if required and returns |result_id|. type_ids| may not be
+// empty. It may not contain result ids of OpTypeFunction instructions.
+// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
+// |result_id| must not be equal to 0. Updates module's id bound to accommodate
+// for |result_id|.
+uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
+ uint32_t result_id,
+ const std::vector<uint32_t>& type_ids);
+
+// Returns a result id of an OpTypeInt instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
+ bool is_signed);
+
+// Returns a result id of an OpTypeFloat instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width);
+
+// Returns a result id of an OpTypeBool instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context);
+
+// Returns a result id of an OpTypeVector instruction if present. Returns 0
+// otherwise. |component_type_id| must be a valid result id of an OpTypeInt,
+// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be
+// in the range [2, 4].
+uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
+ uint32_t component_type_id, uint32_t element_count);
+
+// Returns a result id of an OpTypeStruct instruction if present. Returns 0
+// otherwise. |component_type_ids| may not contain a result id of an
+// OpTypeFunction.
+uint32_t MaybeGetStructType(opt::IRContext* ir_context,
+ const std::vector<uint32_t>& component_type_ids);
+
+// Recursive definition is the following:
+// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns
+// a result id of the following constants (depending on the type): int -> 0,
+// float -> 0.0, bool -> false.
+// - otherwise, returns a result id of an OpConstantComposite instruction.
+// Every component of the composite constant is looked up by calling this
+// function with the type id of that component.
+// Returns 0 if no such instruction is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetZeroConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ uint32_t scalar_or_composite_type_id, bool is_irrelevant);
+
+// Returns the result id of an OpConstant instruction. |scalar_type_id| must be
+// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
+// instruction is present in the module. The returned id either participates in
+// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetScalarConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+ bool is_irrelevant);
+
+// Returns the result id of an OpConstantComposite instruction.
+// |composite_type_id| must be a result id of a composite type (i.e. vector,
+// matrix, struct or array). Returns 0 if no such instruction is present in the
+// module. The returned id either participates in IdIsIrrelevant fact or not,
+// depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetCompositeConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+ bool is_irrelevant);
+
+// Returns the result id of an OpConstant instruction of integral type.
+// Returns 0 if no such instruction or type is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetIntegerConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+ bool is_irrelevant);
+
+// Returns the id of a 32-bit integer constant in the module with type
+// |int_type_id| and value |value|, or 0 if no such constant exists in the
+// module. |int_type_id| must exist in the module and it must correspond to a
+// 32-bit integer type.
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+ uint32_t value,
+ uint32_t int_type_id);
+
+// Returns the result id of an OpConstant instruction of floating-point type.
+// Returns 0 if no such instruction or type is present in the module.
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetFloatConstant(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant);
+
+// Returns the id of a boolean constant with value |value| if it exists in the
+// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant
+// fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetBoolConstant(
+ opt::IRContext* context,
+ const TransformationContext& transformation_context, bool value,
+ bool is_irrelevant);
+
+// Creates a new OpTypeInt instruction in the module. Updates module's id bound
+// to accommodate for |result_id|.
+void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t width, bool is_signed);
+
+// Creates a new OpTypeFloat instruction in the module. Updates module's id
+// bound to accommodate for |result_id|.
+void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t width);
+
+// Creates a new OpTypeVector instruction in the module. |component_type_id|
+// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool
+// instruction in the module. |element_count| must be in the range [2, 4].
+// Updates module's id bound to accommodate for |result_id|.
+void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
+ uint32_t component_type_id, uint32_t element_count);
+
+// Creates a new OpTypeStruct instruction in the module. Updates module's id
+// bound to accommodate for |result_id|. |component_type_ids| may not contain
+// a result id of an OpTypeFunction.
+void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
+ const std::vector<uint32_t>& component_type_ids);
+
+// Returns a bit pattern that represents a floating-point |value|.
+inline uint32_t FloatToWord(float value) {
+ uint32_t result;
+ memcpy(&result, &value, sizeof(uint32_t));
+ return result;
+}
+
+// Returns true if any of the following is true:
+// - |type1_id| and |type2_id| are the same id
+// - |type1_id| and |type2_id| refer to integer scalar or vector types, only
+// differing by their signedness.
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+ uint32_t type2_id);
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
index b801626..26b8672 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
@@ -39,6 +39,7 @@
// where warnings are ignored.
#include "google/protobuf/util/json_util.h"
+#include "google/protobuf/util/message_differencer.h"
#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
#if defined(__clang__)
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index 775b2ad..3aa5675 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -170,6 +170,7 @@
FactFunctionIsLivesafe function_is_livesafe_fact = 4;
FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
FactIdEquation id_equation_fact = 6;
+ FactIdIsIrrelevant id_is_irrelevant = 7;
}
}
@@ -182,6 +183,7 @@
// can be made to this block.
uint32 block_id = 1;
+
}
message FactConstantUniform {
@@ -222,6 +224,7 @@
// functions.
uint32 function_id = 1;
+
}
message FactIdEquation {
@@ -249,6 +252,18 @@
}
+message FactIdIsIrrelevant {
+
+ // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
+ // change the semantics of the module). This implies that a use of this id
+ // can later be replaced with some other id of the same type, or the
+ // definition of |result_id| can be changed so that it yields a different value.
+
+ // An irrelevant id.
+ uint32 result_id = 1;
+
+}
+
message FactPointeeValueIsIrrelevant {
// Records the fact that value of the data pointed to by a pointer id does
@@ -258,6 +273,7 @@
// A result id of pointer type
uint32 pointer_id = 1;
+
}
message AccessChainClampingInfo {
@@ -341,40 +357,56 @@
TransformationAddTypePointer add_type_pointer = 10;
TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
TransformationAddDeadContinue add_dead_continue = 12;
- TransformationCopyObject copy_object = 13;
- TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
- TransformationSetSelectionControl set_selection_control = 15;
- TransformationCompositeConstruct composite_construct = 16;
- TransformationSetLoopControl set_loop_control = 17;
- TransformationSetFunctionControl set_function_control = 18;
- TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
- TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
- TransformationCompositeExtract composite_extract = 21;
- TransformationVectorShuffle vector_shuffle = 22;
- TransformationOutlineFunction outline_function = 23;
- TransformationMergeBlocks merge_blocks = 24;
- TransformationAddTypeVector add_type_vector = 25;
- TransformationAddTypeArray add_type_array = 26;
- TransformationAddTypeMatrix add_type_matrix = 27;
- TransformationAddTypeStruct add_type_struct = 28;
- TransformationAddTypeFunction add_type_function = 29;
- TransformationAddConstantComposite add_constant_composite = 30;
- TransformationAddGlobalVariable add_global_variable = 31;
- TransformationAddGlobalUndef add_global_undef = 32;
- TransformationAddFunction add_function = 33;
- TransformationAddDeadBlock add_dead_block = 34;
- TransformationAddLocalVariable add_local_variable = 35;
- TransformationLoad load = 36;
- TransformationStore store = 37;
- TransformationFunctionCall function_call = 38;
- TransformationAccessChain access_chain = 39;
- TransformationEquationInstruction equation_instruction = 40;
- TransformationSwapCommutableOperands swap_commutable_operands = 41;
- TransformationPermuteFunctionParameters permute_function_parameters = 42;
- TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
- TransformationAddConstantNull add_constant_null = 44;
- TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45;
- TransformationAdjustBranchWeights adjust_branch_weights = 46;
+ TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
+ TransformationSetSelectionControl set_selection_control = 14;
+ TransformationCompositeConstruct composite_construct = 15;
+ TransformationSetLoopControl set_loop_control = 16;
+ TransformationSetFunctionControl set_function_control = 17;
+ TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
+ TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
+ TransformationCompositeExtract composite_extract = 20;
+ TransformationVectorShuffle vector_shuffle = 21;
+ TransformationOutlineFunction outline_function = 22;
+ TransformationMergeBlocks merge_blocks = 23;
+ TransformationAddTypeVector add_type_vector = 24;
+ TransformationAddTypeArray add_type_array = 25;
+ TransformationAddTypeMatrix add_type_matrix = 26;
+ TransformationAddTypeStruct add_type_struct = 27;
+ TransformationAddTypeFunction add_type_function = 28;
+ TransformationAddConstantComposite add_constant_composite = 29;
+ TransformationAddGlobalVariable add_global_variable = 30;
+ TransformationAddGlobalUndef add_global_undef = 31;
+ TransformationAddFunction add_function = 32;
+ TransformationAddDeadBlock add_dead_block = 33;
+ TransformationAddLocalVariable add_local_variable = 34;
+ TransformationLoad load = 35;
+ TransformationStore store = 36;
+ TransformationFunctionCall function_call = 37;
+ TransformationAccessChain access_chain = 38;
+ TransformationEquationInstruction equation_instruction = 39;
+ TransformationSwapCommutableOperands swap_commutable_operands = 40;
+ TransformationPermuteFunctionParameters permute_function_parameters = 41;
+ TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
+ TransformationAddConstantNull add_constant_null = 43;
+ TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
+ TransformationAdjustBranchWeights adjust_branch_weights = 45;
+ TransformationPushIdThroughVariable push_id_through_variable = 46;
+ TransformationAddSpecConstantOp add_spec_constant_op = 47;
+ TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
+ TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
+ TransformationPermutePhiOperands permute_phi_operands = 50;
+ TransformationAddParameter add_parameter = 51;
+ TransformationAddCopyMemory add_copy_memory = 52;
+ TransformationInvertComparisonOperator invert_comparison_operator = 53;
+ TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
+ TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
+ TransformationRecordSynonymousConstants record_synonymous_constants = 56;
+ TransformationAddSynonym add_synonym = 57;
+ TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
+ TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
+ TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
+ TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
+ TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
// Add additional option using the next available number.
}
}
@@ -385,6 +417,13 @@
// Adds an access chain instruction based on a given pointer and indices.
+ // When accessing a struct, the corresponding indices must be 32-bit integer constants.
+ // For any other composite, the indices can be any 32-bit integer, and the transformation
+ // adds two instructions for each such index to clamp it to the bound, as follows:
+ //
+ // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+ // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+
// Result id for the access chain
uint32 fresh_id = 1;
@@ -398,21 +437,31 @@
// OpAccessChain instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 4;
+ // Additional fresh ids, required to clamp index variables. A pair is needed
+ // for each access to a non-struct composite.
+ repeated UInt32Pair fresh_ids_for_clamping = 5;
+
}
message TransformationAddConstantBoolean {
// Supports adding the constants true and false to a module, which may be
// necessary in order to enable other transformations if they are not present.
+ // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
uint32 fresh_id = 1;
bool is_true = 2;
+ // If the constant should be marked as irrelevant.
+ bool is_irrelevant = 3;
+
}
message TransformationAddConstantComposite {
// Adds a constant of the given composite type to the module.
+ // Also, creates an IdIsIrrelevant fact about |fresh_id| if
+ // |is_irrelevant| is true.
// Fresh id for the composite
uint32 fresh_id = 1;
@@ -423,6 +472,9 @@
// Constituent ids for the composite
repeated uint32 constituent_id = 3;
+ // If the constant should be marked as irrelevant.
+ bool is_irrelevant = 4;
+
}
message TransformationAddConstantNull {
@@ -440,6 +492,8 @@
message TransformationAddConstantScalar {
// Adds a constant of the given scalar type.
+ // Also, creates an IdIsIrrelevant fact about
+ // |fresh_id| if |is_irrelevant| is true.
// Id for the constant
uint32 fresh_id = 1;
@@ -450,6 +504,33 @@
// Value of the constant
repeated uint32 word = 3;
+ // If the constant should be marked as irrelevant.
+ bool is_irrelevant = 4;
+
+}
+
+message TransformationAddCopyMemory {
+
+ // Adds an OpCopyMemory instruction into the module.
+ // Creates either a global or a local variable (based on
+ // |storage_class| field) to copy the target into.
+
+ // OpCopyMemory will be inserted before this instruction.
+ InstructionDescriptor instruction_descriptor = 1;
+
+ // Fresh id to copy memory into.
+ uint32 fresh_id = 2;
+
+ // Source to copy memory from.
+ uint32 source_id = 3;
+
+ // Storage class for the target variable. Can be either Function or Private.
+ uint32 storage_class = 4;
+
+ // Result id for the variable's initializer operand. Its type must be equal to
+ // variable's pointee type.
+ uint32 initializer_id = 5;
+
}
message TransformationAddDeadBlock {
@@ -584,6 +665,18 @@
}
+message TransformationAddImageSampleUnusedComponents {
+
+ // A transformation that adds unused components to an image sample coordinate.
+
+ // An vector id with the original coordinate and the unused components.
+ uint32 coordinate_with_unused_components_id = 1;
+
+ // A descriptor for an image sample instruction.
+ InstructionDescriptor instruction_descriptor = 2;
+
+}
+
message TransformationAddLocalVariable {
// Adds a local variable of the given type (which must be a pointer with
@@ -618,6 +711,103 @@
}
+message TransformationAddParameter {
+
+ // Adds a new parameter into the function.
+
+ // Result id of the function to add parameters to.
+ uint32 function_id = 1;
+
+ // Fresh id for a new parameter.
+ uint32 parameter_fresh_id = 2;
+
+ // Result id of the instruction, used to initializer new parameter
+ // in function calls. Type id of that instruction is the type id of the parameter.
+ // It may not be OpTypeVoid.
+ uint32 initializer_id = 3;
+
+ // A fresh id for a new function type. This might not be used
+ // if a required function type already exists or if we can change
+ // the old function type.
+ uint32 function_type_fresh_id = 4;
+
+}
+
+message TransformationAddRelaxedDecoration {
+
+ // Applies OpDecorate RelaxedPrecision to the given result id
+
+ // Result id to be decorated
+ uint32 result_id = 1;
+
+}
+
+message TransformationAddSpecConstantOp {
+
+ // Adds OpSpecConstantOp into the module.
+
+ // Result id for the new instruction.
+ uint32 fresh_id = 1;
+
+ // Type id for the new instruction.
+ uint32 type_id = 2;
+
+ // Opcode operand of the OpSpecConstantOp instruction.
+ uint32 opcode = 3;
+
+ // Operands of the |opcode| instruction.
+ repeated InstructionOperand operand = 4;
+
+}
+
+message TransformationAddSynonym {
+
+ // Adds a |synonymous_instruction| before |insert_before| instruction with
+ // and creates a fact that |result_id| and the result id of |synonymous_instruction|
+ // are synonymous.
+
+ // Result id of the first synonym.
+ uint32 result_id = 1;
+
+ // Type of the synonym to apply. Some types might produce instructions
+ // with commutative operands. Such types do not specify the order of the
+ // operands since we have a special transformation to swap commutable operands.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
+ // Consider adding more types here.
+ enum SynonymType {
+ // New synonym is derived by adding zero to the |result_id|.
+ ADD_ZERO = 0;
+
+ // New synonym is derived by subtracting zero from the |result_id|.
+ SUB_ZERO = 1;
+
+ // New synonym is derived by multiplying |result_id| by one.
+ MUL_ONE = 2;
+
+ // New synonym is derived by applying OpCopyObject instruction to |result_id|.
+ COPY_OBJECT = 3;
+
+ // New synonym is derived by applying OpLogicalOr to |result_id| with the second
+ // operand being 'false'.
+ LOGICAL_OR = 4;
+
+ // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
+ // operand being 'true'.
+ LOGICAL_AND = 5;
+ }
+
+ // Type of the synonym to create. See SynonymType for more details.
+ SynonymType synonym_type = 2;
+
+ // Fresh result id for a created synonym.
+ uint32 synonym_fresh_id = 3;
+
+ // An instruction to insert a new synonym before.
+ InstructionDescriptor insert_before = 4;
+
+}
+
message TransformationAddTypeArray {
// Adds an array type of the given element type and size to the module
@@ -809,23 +999,6 @@
}
-message TransformationCopyObject {
-
- // A transformation that introduces an OpCopyObject instruction to make a
- // copy of an object.
-
- // Id of the object to be copied
- uint32 object = 1;
-
- // A descriptor for an instruction in a block before which the new
- // OpCopyObject instruction should be inserted
- InstructionDescriptor instruction_to_insert_before = 2;
-
- // A fresh id for the copied object
- uint32 fresh_id = 3;
-
-}
-
message TransformationEquationInstruction {
// A transformation that adds an instruction to the module that defines an
@@ -871,6 +1044,22 @@
}
+message TransformationInvertComparisonOperator {
+
+ // For some instruction with result id |operator_id| that
+ // represents a binary comparison operator (e.g. <, >, <=), this transformation
+ // will replace that instruction's result id with |fresh_id|,
+ // invert the opcode (< will become >=) and insert OpLogicalNot
+ // instruction with result id |operator_id| below.
+
+ // Result id of the instruction to invert.
+ uint32 operator_id = 1;
+
+ // Fresh id that will be used by the operator after the inversion.
+ uint32 fresh_id = 2;
+
+}
+
message TransformationLoad {
// Transformation that adds an OpLoad instruction from a pointer into an id.
@@ -966,12 +1155,10 @@
// Function, whose parameters will be permuted
uint32 function_id = 1;
- // |new_type_id| is a result id of a valid OpTypeFunction instruction.
- // New type is valid if:
- // - it has the same number of operands as the old one
- // - function's result type is the same as the old one
- // - function's arguments are permuted according to |permutation| vector
- uint32 new_type_id = 2;
+ // Fresh id for a new type of the function. This might not be used
+ // if a required function type already exists or if we can change
+ // the old function type.
+ uint32 function_type_fresh_id = 2;
// An array of size |n|, where |n| is a number of arguments to a function
// with |function_id|. For each i: 0 <= permutation[i] < n.
@@ -983,6 +1170,83 @@
}
+message TransformationPermutePhiOperands {
+
+ // Permutes operands of some OpPhi instruction.
+
+ // Result id of the instruction to apply the transformation to.
+ uint32 result_id = 1;
+
+ // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
+ // of operands of the OpPhi instruction with |result_id|.
+ repeated uint32 permutation = 2;
+
+}
+
+message TransformationPushIdThroughVariable {
+
+ // A transformation that makes |value_synonym_id| and |value_id| to be
+ // synonymous by storing |value_id| into |variable_id| and
+ // loading |variable_id| to |value_synonym_id|.
+
+ // The value to be stored.
+ uint32 value_id = 1;
+
+ // A fresh id for the result of the load instruction.
+ uint32 value_synonym_id = 2;
+
+ // A fresh id for the variable to be stored to.
+ uint32 variable_id = 3;
+
+ // Constant to initialize the variable from.
+ uint32 initializer_id = 4;
+
+ // The variable storage class (global or local).
+ uint32 variable_storage_class = 5;
+
+ // A descriptor for an instruction which the new OpStore
+ // and OpLoad instructions might be inserted before.
+ InstructionDescriptor instruction_descriptor = 6;
+
+}
+
+message TransformationRecordSynonymousConstants {
+
+ // A transformation that, given the IDs to two synonymous constants,
+ // records the fact that they are synonymous. The module is not changed.
+ // Two constants are synonymous if:
+ // - they have the same type (ignoring the presence of integer sign)
+ // - they have the same opcode (one of OpConstant, OpConstantTrue,
+ // OpConstantFalse, OpConstantNull)
+ // - they have the same value
+ // If the types are the same, OpConstantNull is equivalent to
+ // OpConstantFalse or OpConstant with value zero.
+
+ // The id of a constant
+ uint32 constant1_id = 1;
+
+ // The id of the synonym
+ uint32 constant2_id = 2;
+
+}
+
+message TransformationReplaceParameterWithGlobal {
+
+ // Removes parameter with result id |parameter_id| from its function
+ // and creates a global variable to pass its value to the function instead.
+
+ // Fresh id for a new function type. This might not be used if a required
+ // function type already exists or if we can change the old function type.
+ uint32 function_type_fresh_id = 2;
+
+ // Result id of the OpFunctionParameter instruction to remove.
+ uint32 parameter_id = 3;
+
+ // Fresh id of a global variable used to pass parameter's value to the function.
+ uint32 global_variable_fresh_id = 4;
+
+}
+
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with
@@ -1024,6 +1288,39 @@
}
+message TransformationReplaceCopyMemoryWithLoadStore {
+
+ // A transformation that replaces instructions OpCopyMemory with loading
+ // the source variable to an intermediate value and storing this value into the
+ // target variable of the original OpCopyMemory instruction.
+
+ // The intermediate value.
+ uint32 fresh_id = 1;
+
+ // The instruction descriptor to OpCopyMemory. It is necessary, because
+ // OpCopyMemory doesn't have a result id.
+ InstructionDescriptor copy_memory_instruction_descriptor = 2;
+}
+
+message TransformationReplaceCopyObjectWithStoreLoad {
+
+ // A transformation that replaces instruction OpCopyObject with
+ // storing into a new variable and immediately loading from this
+ // variable to |result_id| of the original OpCopyObject instruction.
+
+ // The result id of initial OpCopyObject instruction
+ uint32 copy_object_result_id = 1;
+
+ // A fresh id for the variable to be stored to.
+ uint32 fresh_variable_id = 2;
+
+ // The variable storage class (Function or Private).
+ uint32 variable_storage_class = 3;
+
+ // Constant to initialize the variable with.
+ uint32 variable_initializer_id = 4;
+}
+
message TransformationReplaceIdWithSynonym {
// Replaces a use of an id with an id that is known to be synonymous, e.g.
@@ -1037,6 +1334,70 @@
}
+message TransformationReplaceLinearAlgebraInstruction {
+
+ // Replaces a linear algebra instruction with its
+ // mathematical definition.
+
+ // The fresh ids needed to apply the transformation.
+ repeated uint32 fresh_ids = 1;
+
+ // A descriptor for a linear algebra instruction.
+ // This transformation is only applicable if the described instruction has one of the following opcodes.
+ // Supported:
+ // OpVectorTimesScalar
+ // OpMatrixTimesScalar
+ // OpVectorTimesMatrix
+ // OpMatrixTimesVector
+ // OpMatrixTimesMatrix
+ // OpDot
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+ // Right now we only support certain operations. When this issue is addressed
+ // the supporting comments can be removed.
+ // To be supported in the future:
+ // OpTranspose
+ // OpOuterProduct
+ InstructionDescriptor instruction_descriptor = 2;
+
+}
+
+message TransformationReplaceLoadStoreWithCopyMemory {
+ // A transformation that takes a pair of instruction descriptors
+ // to OpLoad and OpStore that have the same intermediate value
+ // and replaces the OpStore with an equivalent OpCopyMemory.
+
+ // The instruction descriptor to OpLoad
+ InstructionDescriptor load_instruction_descriptor = 1;
+
+ // The instruction descriptor to OpStore
+ InstructionDescriptor store_instruction_descriptor = 2;
+}
+
+message TransformationReplaceParamsWithStruct {
+
+ // Replaces parameters of the function with a struct containing
+ // values of those parameters.
+
+ // Result ids of parameters to replace.
+ repeated uint32 parameter_id = 1;
+
+ // Fresh id for a new function type. This might be unused if the required type
+ // already exists in the module or if we can change the old type.
+ uint32 fresh_function_type_id = 2;
+
+ // Fresh id for a new struct function parameter to be used as a replacement.
+ uint32 fresh_parameter_id = 3;
+
+ // Fresh ids for struct objects containing values of replaced parameters.
+ // This map contains a fresh id for at least every result id of a relevant
+ // OpFunctionCall instruction.
+ //
+ // While maps are not fully deterministic, the way this map is used does not
+ // exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
+ map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
+
+}
+
message TransformationSetFunctionControl {
// A transformation that sets the function control operand of an OpFunction
@@ -1154,6 +1515,21 @@
}
+message TransformationSwapConditionalBranchOperands {
+
+ // Swaps label ids in OpBranchConditional instruction.
+ // Additionally, inverts the guard and swaps branch weights
+ // if present.
+
+ // Descriptor of the instruction to swap operands of.
+ InstructionDescriptor instruction_descriptor = 1;
+
+ // Fresh result id for the OpLogicalNot instruction, used
+ // to invert the guard.
+ uint32 fresh_id = 2;
+
+}
+
message TransformationToggleAccessChainInstruction {
// A transformation that toggles an access chain instruction.
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
index 6312cba..bc680d8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.cpp
@@ -65,12 +65,20 @@
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
const protobufs::TransformationSequence& transformation_sequence_in,
- std::vector<uint32_t>* binary_out,
+ uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const {
// Check compatibility between the library version being linked with and the
// header files being used.
GOOGLE_PROTOBUF_VERIFY_VERSION;
+ if (num_transformations_to_apply >
+ static_cast<uint32_t>(transformation_sequence_in.transformation_size())) {
+ impl_->consumer(SPV_MSG_ERROR, nullptr, {},
+ "The number of transformations to be replayed must not "
+ "exceed the size of the transformation sequence.");
+ return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested;
+ }
+
spvtools::SpirvTools tools(impl_->target_env);
if (!tools.IsValid()) {
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
@@ -104,7 +112,13 @@
impl_->validator_options);
// Consider the transformation proto messages in turn.
+ uint32_t counter = 0;
for (auto& message : transformation_sequence_in.transformation()) {
+ if (counter >= num_transformations_to_apply) {
+ break;
+ }
+ counter++;
+
auto transformation = Transformation::FromMessage(message);
// Check whether the transformation can be applied.
diff --git a/third_party/SPIRV-Tools/source/fuzz/replayer.h b/third_party/SPIRV-Tools/source/fuzz/replayer.h
index e77d840..d6395aa 100644
--- a/third_party/SPIRV-Tools/source/fuzz/replayer.h
+++ b/third_party/SPIRV-Tools/source/fuzz/replayer.h
@@ -34,6 +34,7 @@
kFailedToCreateSpirvToolsInterface,
kInitialBinaryInvalid,
kReplayValidationFailure,
+ kTooManyTransformationsRequested,
};
// Constructs a replayer from the given target environment.
@@ -52,16 +53,17 @@
// invoked once for each message communicated from the library.
void SetMessageConsumer(MessageConsumer consumer);
- // Transforms |binary_in| to |binary_out| by attempting to apply the
- // transformations from |transformation_sequence_in|. Initial facts about the
- // input binary and the context in which it will execute are provided via
- // |initial_facts|. The transformations that were successfully applied are
- // returned via |transformation_sequence_out|.
+ // Transforms |binary_in| to |binary_out| by attempting to apply the first
+ // |num_transformations_to_apply| transformations from
+ // |transformation_sequence_in|. Initial facts about the input binary and the
+ // context in which it will execute are provided via |initial_facts|. The
+ // transformations that were successfully applied are returned via
+ // |transformation_sequence_out|.
ReplayerResultStatus Run(
const std::vector<uint32_t>& binary_in,
const protobufs::FactSequence& initial_facts,
const protobufs::TransformationSequence& transformation_sequence_in,
- std::vector<uint32_t>* binary_out,
+ uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
protobufs::TransformationSequence* transformation_sequence_out) const;
private:
diff --git a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
index 002e8a7..829df63 100644
--- a/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/shrinker.cpp
@@ -121,10 +121,13 @@
// succeeds, (b) get the binary that results from running these
// transformations, and (c) get the subsequence of the initial transformations
// that actually apply (in principle this could be a strict subsequence).
- if (Replayer(impl_->target_env, impl_->validate_during_replay,
- impl_->validator_options)
- .Run(binary_in, initial_facts, transformation_sequence_in,
- ¤t_best_binary, ¤t_best_transformations) !=
+ Replayer replayer(impl_->target_env, impl_->validate_during_replay,
+ impl_->validator_options);
+ replayer.SetMessageConsumer(impl_->consumer);
+ if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
+ static_cast<uint32_t>(
+ transformation_sequence_in.transformation_size()),
+ ¤t_best_binary, ¤t_best_transformations) !=
Replayer::ReplayerResultStatus::kComplete) {
return ShrinkerResultStatus::kReplayFailed;
}
@@ -184,7 +187,8 @@
// Remove a chunk of transformations according to the current index and
// chunk size.
auto transformations_with_chunk_removed =
- RemoveChunk(current_best_transformations, chunk_index, chunk_size);
+ RemoveChunk(current_best_transformations,
+ static_cast<uint32_t>(chunk_index), chunk_size);
// Replay the smaller sequence of transformations to get a next binary and
// transformation sequence. Note that the transformations arising from
@@ -193,10 +197,11 @@
// transformations inapplicable.
std::vector<uint32_t> next_binary;
protobufs::TransformationSequence next_transformation_sequence;
- if (Replayer(impl_->target_env, impl_->validate_during_replay,
- impl_->validator_options)
- .Run(binary_in, initial_facts, transformations_with_chunk_removed,
- &next_binary, &next_transformation_sequence) !=
+ if (replayer.Run(
+ binary_in, initial_facts, transformations_with_chunk_removed,
+ static_cast<uint32_t>(
+ transformations_with_chunk_removed.transformation_size()),
+ &next_binary, &next_transformation_sequence) !=
Replayer::ReplayerResultStatus::kComplete) {
// Replay should not fail; if it does, we need to abort shrinking.
return ShrinkerResultStatus::kReplayFailed;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index 8b84169..a31384f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -22,14 +22,20 @@
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_null.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_copy_memory.h"
#include "source/fuzz/transformation_add_dead_block.h"
#include "source/fuzz/transformation_add_dead_break.h"
#include "source/fuzz/transformation_add_dead_continue.h"
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_parameter.h"
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+#include "source/fuzz/transformation_add_spec_constant_op.h"
+#include "source/fuzz/transformation_add_synonym.h"
#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
@@ -43,17 +49,26 @@
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
-#include "source/fuzz/transformation_copy_object.h"
#include "source/fuzz/transformation_equation_instruction.h"
#include "source/fuzz/transformation_function_call.h"
+#include "source/fuzz/transformation_invert_comparison_operator.h"
#include "source/fuzz/transformation_load.h"
#include "source/fuzz/transformation_merge_blocks.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+#include "source/fuzz/transformation_push_id_through_variable.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
#include "source/fuzz/transformation_set_function_control.h"
#include "source/fuzz/transformation_set_loop_control.h"
#include "source/fuzz/transformation_set_memory_operands_mask.h"
@@ -61,6 +76,7 @@
#include "source/fuzz/transformation_split_block.h"
#include "source/fuzz/transformation_store.h"
#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/util/make_unique.h"
@@ -87,6 +103,8 @@
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
+ case protobufs::Transformation::TransformationCase::kAddCopyMemory:
+ return MakeUnique<TransformationAddCopyMemory>(message.add_copy_memory());
case protobufs::Transformation::TransformationCase::kAddDeadBlock:
return MakeUnique<TransformationAddDeadBlock>(message.add_dead_block());
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
@@ -102,6 +120,10 @@
case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
return MakeUnique<TransformationAddGlobalVariable>(
message.add_global_variable());
+ case protobufs::Transformation::TransformationCase::
+ kAddImageSampleUnusedComponents:
+ return MakeUnique<TransformationAddImageSampleUnusedComponents>(
+ message.add_image_sample_unused_components());
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
return MakeUnique<TransformationAddLocalVariable>(
message.add_local_variable());
@@ -109,6 +131,16 @@
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(
message.add_no_contraction_decoration());
+ case protobufs::Transformation::TransformationCase::kAddParameter:
+ return MakeUnique<TransformationAddParameter>(message.add_parameter());
+ case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration:
+ return MakeUnique<TransformationAddRelaxedDecoration>(
+ message.add_relaxed_decoration());
+ case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
+ return MakeUnique<TransformationAddSpecConstantOp>(
+ message.add_spec_constant_op());
+ case protobufs::Transformation::TransformationCase::kAddSynonym:
+ return MakeUnique<TransformationAddSynonym>(message.add_synonym());
case protobufs::Transformation::TransformationCase::kAddTypeArray:
return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
@@ -143,13 +175,15 @@
kComputeDataSynonymFactClosure:
return MakeUnique<TransformationComputeDataSynonymFactClosure>(
message.compute_data_synonym_fact_closure());
- case protobufs::Transformation::TransformationCase::kCopyObject:
- return MakeUnique<TransformationCopyObject>(message.copy_object());
case protobufs::Transformation::TransformationCase::kEquationInstruction:
return MakeUnique<TransformationEquationInstruction>(
message.equation_instruction());
case protobufs::Transformation::TransformationCase::kFunctionCall:
return MakeUnique<TransformationFunctionCall>(message.function_call());
+ case protobufs::Transformation::TransformationCase::
+ kInvertComparisonOperator:
+ return MakeUnique<TransformationInvertComparisonOperator>(
+ message.invert_comparison_operator());
case protobufs::Transformation::TransformationCase::kLoad:
return MakeUnique<TransformationLoad>(message.load());
case protobufs::Transformation::TransformationCase::kMergeBlocks:
@@ -163,6 +197,20 @@
kPermuteFunctionParameters:
return MakeUnique<TransformationPermuteFunctionParameters>(
message.permute_function_parameters());
+ case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
+ return MakeUnique<TransformationPermutePhiOperands>(
+ message.permute_phi_operands());
+ case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
+ return MakeUnique<TransformationPushIdThroughVariable>(
+ message.push_id_through_variable());
+ case protobufs::Transformation::TransformationCase::
+ kRecordSynonymousConstants:
+ return MakeUnique<TransformationRecordSynonymousConstants>(
+ message.record_synonymous_constants());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceParameterWithGlobal:
+ return MakeUnique<TransformationReplaceParameterWithGlobal>(
+ message.replace_parameter_with_global());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -171,9 +219,29 @@
kReplaceConstantWithUniform:
return MakeUnique<TransformationReplaceConstantWithUniform>(
message.replace_constant_with_uniform());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceCopyMemoryWithLoadStore:
+ return MakeUnique<TransformationReplaceCopyMemoryWithLoadStore>(
+ message.replace_copy_memory_with_load_store());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceCopyObjectWithStoreLoad:
+ return MakeUnique<TransformationReplaceCopyObjectWithStoreLoad>(
+ message.replace_copy_object_with_store_load());
case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
return MakeUnique<TransformationReplaceIdWithSynonym>(
message.replace_id_with_synonym());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceLinearAlgebraInstruction:
+ return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
+ message.replace_linear_algebra_instruction());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceLoadStoreWithCopyMemory:
+ return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
+ message.replace_load_store_with_copy_memory());
+ case protobufs::Transformation::TransformationCase::
+ kReplaceParamsWithStruct:
+ return MakeUnique<TransformationReplaceParamsWithStruct>(
+ message.replace_params_with_struct());
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
return MakeUnique<TransformationSetFunctionControl>(
message.set_function_control());
@@ -194,6 +262,10 @@
return MakeUnique<TransformationSwapCommutableOperands>(
message.swap_commutable_operands());
case protobufs::Transformation::TransformationCase::
+ kSwapConditionalBranchOperands:
+ return MakeUnique<TransformationSwapConditionalBranchOperands>(
+ message.swap_conditional_branch_operands());
+ case protobufs::Transformation::TransformationCase::
kToggleAccessChainInstruction:
return MakeUnique<TransformationToggleAccessChainInstruction>(
message.toggle_access_chain_instruction());
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
index ff17c36..3366869 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.cpp
@@ -29,7 +29,8 @@
TransformationAccessChain::TransformationAccessChain(
uint32_t fresh_id, uint32_t pointer_id,
const std::vector<uint32_t>& index_id,
- const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+ const protobufs::InstructionDescriptor& instruction_to_insert_before,
+ const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping) {
message_.set_fresh_id(fresh_id);
message_.set_pointer_id(pointer_id);
for (auto id : index_id) {
@@ -37,12 +38,22 @@
}
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
+ for (auto clamping_ids_pair : fresh_ids_for_clamping) {
+ protobufs::UInt32Pair pair;
+ pair.set_first(clamping_ids_pair.first);
+ pair.set_second(clamping_ids_pair.second);
+ *message_.add_fresh_ids_for_clamping() = pair;
+ }
}
bool TransformationAccessChain::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
- // The result id must be fresh
- if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ // Keep track of the fresh ids used to make sure that they are distinct.
+ std::set<uint32_t> fresh_ids_used;
+
+ // The result id must be fresh.
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ message_.fresh_id(), ir_context, &fresh_ids_used)) {
return false;
}
// The pointer id must exist and have a type.
@@ -50,7 +61,7 @@
if (!pointer || !pointer->type_id()) {
return false;
}
- // The type must indeed be a pointer
+ // The type must indeed be a pointer.
auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
if (pointer_type->opcode() != SpvOpTypePointer) {
return false;
@@ -74,9 +85,9 @@
switch (pointer->opcode()) {
case SpvOpConstantNull:
case SpvOpUndef:
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When
- // fuzzing for real we would like an 'assert(false)' here. But we also
- // want to be able to write negative unit tests.
+ assert(
+ false &&
+ "Access chains should not be created from null/undefined pointers");
return false;
default:
break;
@@ -96,23 +107,86 @@
// Start from the base type of the pointer.
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+ int id_pairs_used = 0;
+
// Consider the given index ids in turn.
for (auto index_id : message_.index_id()) {
- // Try to get the integer value associated with this index is. The first
- // component of the result will be false if the id did not correspond to an
- // integer. Otherwise, the integer with which the id is associated is the
- // second component.
- std::pair<bool, uint32_t> maybe_index_value =
- GetIndexValue(ir_context, index_id);
- if (!maybe_index_value.first) {
- // There was no integer: this index is no good.
- return false;
+ // The index value will correspond to the value of the index if the object
+ // is a struct, otherwise the value 0 will be used.
+ uint32_t index_value;
+
+ // Check whether the object is a struct.
+ if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+ SpvOpTypeStruct) {
+ // It is a struct: we need to retrieve the integer value.
+
+ bool successful;
+ std::tie(successful, index_value) =
+ GetIndexValue(ir_context, index_id, subobject_type_id);
+
+ if (!successful) {
+ return false;
+ }
+ } else {
+ // It is not a struct: the index will need clamping.
+
+ if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) {
+ // We don't have enough ids
+ return false;
+ }
+
+ // Get two new ids to use and update the amount used.
+ protobufs::UInt32Pair fresh_ids =
+ message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+ // Valid ids need to have been given
+ if (fresh_ids.first() == 0 || fresh_ids.second() == 0) {
+ return false;
+ }
+
+ // Check that the ids are actually fresh and not already used by this
+ // transformation.
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ fresh_ids.first(), ir_context, &fresh_ids_used) ||
+ !CheckIdIsFreshAndNotUsedByThisTransformation(
+ fresh_ids.second(), ir_context, &fresh_ids_used)) {
+ return false;
+ }
+
+ if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) {
+ return false;
+ }
+
+ // Perform the clamping using the fresh ids at our disposal.
+ auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+ uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+ *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+ ir_context);
+
+ // The module must have an integer constant of value bound-1 of the same
+ // type as the index.
+ if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+ ir_context, bound - 1, index_instruction->type_id())) {
+ return false;
+ }
+
+ // The module must have the definition of bool type to make a comparison.
+ if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
+ return false;
+ }
+
+ // The index is not necessarily a constant, so we may not know its value.
+ // We can use index 0 because the components of a non-struct composite
+ // all have the same type, and index 0 is always in bounds.
+ index_value = 0;
}
+
// Try to walk down the type using this index. This will yield 0 if the
// type is not a composite or the index is out of bounds, and the id of
// the next type otherwise.
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
- ir_context, subobject_type_id, maybe_index_value.second);
+ ir_context, subobject_type_id, index_value);
if (!subobject_type_id) {
// Either the type was not a composite (so that too many indices were
// provided), or the index was out of bounds.
@@ -152,25 +226,105 @@
ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+ uint32_t id_pairs_used = 0;
+
// Go through the index ids in turn.
for (auto index_id : message_.index_id()) {
- // Add the index id to the operands.
- operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
- // Get the integer value associated with the index id.
- uint32_t index_value = GetIndexValue(ir_context, index_id).second;
+ uint32_t index_value;
+
+ // Actual id to be used in the instruction: the original id
+ // or the clamped one.
+ uint32_t new_index_id;
+
+ // Check whether the object is a struct.
+ if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+ SpvOpTypeStruct) {
+ // It is a struct: we need to retrieve the integer value.
+
+ index_value =
+ GetIndexValue(ir_context, index_id, subobject_type_id).second;
+
+ new_index_id = index_id;
+
+ } else {
+ // It is not a struct: the index will need clamping.
+
+ // Get two new ids to use and update the amount used.
+ protobufs::UInt32Pair fresh_ids =
+ message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+ // Perform the clamping using the fresh ids at our disposal.
+ // The module will not be changed if |add_clamping_instructions| is not
+ // set.
+ auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+ uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+ *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+ ir_context);
+
+ auto bound_minus_one_id =
+ fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+ ir_context, bound - 1, index_instruction->type_id());
+
+ assert(bound_minus_one_id &&
+ "A constant of value bound - 1 and the same type as the index "
+ "must exist as a precondition.");
+
+ uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context);
+
+ assert(bool_type_id &&
+ "An OpTypeBool instruction must exist as a precondition.");
+
+ auto int_type_inst =
+ ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+
+ // Clamp the integer and add the corresponding instructions in the module
+ // if |add_clamping_instructions| is set.
+ auto instruction_to_insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), ir_context);
+
+ // Compare the index with the bound via an instruction of the form:
+ // %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
+ fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
+ instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+ // Select the index if in-bounds, otherwise one less than the bound:
+ // %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
+ // %bound_minus_one
+ fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
+ instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpSelect, int_type_inst->result_id(),
+ fresh_ids.second(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
+ {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+ new_index_id = fresh_ids.second();
+
+ index_value = 0;
+ }
+
+ // Add the correct index id to the operands.
+ operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}});
+
// Walk to the next type in the composite object using this index.
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, subobject_type_id, index_value);
}
- // The access chain's result type is a pointer to the composite component that
- // was reached after following all indices. The storage class is that of the
- // original pointer.
+ // The access chain's result type is a pointer to the composite component
+ // that was reached after following all indices. The storage class is that
+ // of the original pointer.
uint32_t result_type = fuzzerutil::MaybeGetPointerType(
ir_context, subobject_type_id,
static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
- // Add the access chain instruction to the module, and update the module's id
- // bound.
+ // Add the access chain instruction to the module, and update the module's
+ // id bound.
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
FindInstruction(message_.instruction_to_insert_before(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(
@@ -180,8 +334,8 @@
// Conservatively invalidate all analyses.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
- // If the base pointer's pointee value was irrelevant, the same is true of the
- // pointee value of the result of this access chain.
+ // If the base pointer's pointee value was irrelevant, the same is true of
+ // the pointee value of the result of this access chain.
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
message_.pointer_id())) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -196,21 +350,61 @@
}
std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
- opt::IRContext* ir_context, uint32_t index_id) const {
- auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
- if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
- // allow non-constant indices when looking up non-structs, using clamping
- // to ensure they are in-bounds.
+ opt::IRContext* ir_context, uint32_t index_id,
+ uint32_t object_type_id) const {
+ if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
return {false, 0};
}
+ auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+ uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+ *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
+
+ // The index must be a constant
+ if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+ return {false, 0};
+ }
+
+ // The index must be in bounds.
+ uint32_t value = index_instruction->GetSingleWordInOperand(0);
+
+ if (value >= bound) {
+ return {false, 0};
+ }
+
+ return {true, value};
+}
+
+bool TransformationAccessChain::ValidIndexToComposite(
+ opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) {
+ auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id);
+ // The object being indexed must be a composite.
+ if (!spvOpcodeIsComposite(object_type_def->opcode())) {
+ return false;
+ }
+
+ // Get the defining instruction of the index.
+ auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+ if (!index_instruction) {
+ return false;
+ }
+
+ // The index type must be 32-bit integer.
auto index_type =
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
if (index_type->opcode() != SpvOpTypeInt ||
index_type->GetSingleWordInOperand(0) != 32) {
- return {false, 0};
+ return false;
}
- return {true, index_instruction->GetSingleWordInOperand(0)};
+
+ // If the object being traversed is a struct, the id must correspond to an
+ // in-bound constant.
+ if (object_type_def->opcode() == SpvOpTypeStruct) {
+ if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+ return false;
+ }
+ }
+ return true;
}
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
index 9306a59..db5b8e6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_access_chain.h
@@ -33,20 +33,28 @@
TransformationAccessChain(
uint32_t fresh_id, uint32_t pointer_id,
const std::vector<uint32_t>& index_id,
- const protobufs::InstructionDescriptor& instruction_to_insert_before);
+ const protobufs::InstructionDescriptor& instruction_to_insert_before,
+ const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping =
+ {});
- // - |message_.fresh_id| must be fresh
+ // - |message_.fresh_id| must be fresh.
// - |message_.instruction_to_insert_before| must identify an instruction
- // before which it is legitimate to insert an OpAccessChain instruction
+ // before which it is legitimate to insert an OpAccessChain instruction.
// - |message_.pointer_id| must be a result id with pointer type that is
// available (according to dominance rules) at the insertion point.
- // - The pointer must not be OpConstantNull or OpUndef
- // - |message_.index_id| must be a sequence of ids of 32-bit integer constants
+ // - The pointer must not be OpConstantNull or OpUndef.
+ // - |message_.index_id| must be a sequence of ids of 32-bit integers
// such that it is possible to walk the pointee type of
- // |message_.pointer_id| using these indices, remaining in-bounds.
+ // |message_.pointer_id| using these indices.
+ // - All indices used to access a struct must be OpConstant.
+ // - The indices used to index non-struct composites will be clamped to be
+ // in bound. Enough fresh ids must be given in
+ // |message_.fresh_id_for_clamping| to perform clamping (2 for
+ // each index accessing a non-struct). This requires the bool type and
+ // a constant of value (bound - 1) to be declared in the module.
// - If type t is the final type reached by walking these indices, the module
// must include an instruction "OpTypePointer SC %t" where SC is the storage
- // class associated with |message_.pointer_id|
+ // class associated with |message_.pointer_id|.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
@@ -58,6 +66,9 @@
// the indices in |message_.index_id|, and with the same storage class as
// |message_.pointer_id|.
//
+ // For each of the indices traversing non-struct composites, two clamping
+ // instructions are added using ids in |message_.fresh_id_for_clamping|.
+ //
// If the fact manager in |transformation_context| reports that
// |message_.pointer_id| has an irrelevant pointee value, then the fact that
// |message_.fresh_id| (the result of the access chain) also has an irrelevant
@@ -68,11 +79,21 @@
protobufs::Transformation ToMessage() const override;
private:
- // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
- // constant. Otherwise, returns {true, value}, where value is the value of
- // the 32-bit integer constant to which |index_id| corresponds.
+ // Returns {false, 0} in each of the following cases:
+ // - |index_id| does not correspond to a 32-bit integer constant
+ // - the object being indexed is not a composite type
+ // - the constant at |index_id| is out of bounds.
+ // Otherwise, returns {true, value}, where value is the value of the constant
+ // at |index_id|.
std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
- uint32_t index_id) const;
+ uint32_t index_id,
+ uint32_t object_type_id) const;
+
+ // Returns true if |index_id| corresponds, in the given context, to a 32-bit
+ // integer which can be used to index an object of the type specified by
+ // |object_type_id|. Returns false otherwise.
+ static bool ValidIndexToComposite(opt::IRContext* ir_context,
+ uint32_t index_id, uint32_t object_type_id);
protobufs::TransformationAccessChain message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
index 1930f7e..904ad61 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.cpp
@@ -25,34 +25,36 @@
: message_(message) {}
TransformationAddConstantBoolean::TransformationAddConstantBoolean(
- uint32_t fresh_id, bool is_true) {
+ uint32_t fresh_id, bool is_true, bool is_irrelevant) {
message_.set_fresh_id(fresh_id);
message_.set_is_true(is_true);
+ message_.set_is_irrelevant(is_irrelevant);
}
bool TransformationAddConstantBoolean::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
- opt::analysis::Bool bool_type;
- if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
- // No OpTypeBool is present.
- return false;
- }
- return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
+ return fuzzerutil::MaybeGetBoolType(ir_context) != 0 &&
+ fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
}
void TransformationAddConstantBoolean::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- opt::analysis::Bool bool_type;
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
// Add the boolean constant to the module, ensuring the module's id bound is
// high enough.
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
ir_context->module()->AddGlobalValue(
message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
- message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
+ message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context));
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
+
+ if (message_.is_irrelevant()) {
+ transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+ message_.fresh_id());
+ }
}
protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
index 5d876cf..d2f9e9a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_boolean.h
@@ -28,7 +28,8 @@
explicit TransformationAddConstantBoolean(
const protobufs::TransformationAddConstantBoolean& message);
- TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
+ TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true,
+ bool is_irrelevant);
// - |message_.fresh_id| must not be used by the module.
// - The module must already contain OpTypeBool.
@@ -38,6 +39,8 @@
// - Adds OpConstantTrue (OpConstantFalse) to the module with id
// |message_.fresh_id| if |message_.is_true| holds (does not hold).
+ // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant|
+ // is true.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
index ae34b26..99d88b4 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.cpp
@@ -28,9 +28,10 @@
TransformationAddConstantComposite::TransformationAddConstantComposite(
uint32_t fresh_id, uint32_t type_id,
- const std::vector<uint32_t>& constituent_ids) {
+ const std::vector<uint32_t>& constituent_ids, bool is_irrelevant) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
+ message_.set_is_irrelevant(is_irrelevant);
for (auto constituent_id : constituent_ids) {
message_.add_constituent_id(constituent_id);
}
@@ -104,7 +105,8 @@
}
void TransformationAddConstantComposite::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
opt::Instruction::OperandList in_operands;
for (auto constituent_id : message_.constituent_id()) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
@@ -117,6 +119,11 @@
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
+
+ if (message_.is_irrelevant()) {
+ transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+ message_.fresh_id());
+ }
}
protobufs::Transformation TransformationAddConstantComposite::ToMessage()
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
index 4fec561..2dddbfb 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_composite.h
@@ -32,7 +32,7 @@
TransformationAddConstantComposite(
uint32_t fresh_id, uint32_t type_id,
- const std::vector<uint32_t>& constituent_ids);
+ const std::vector<uint32_t>& constituent_ids, bool is_irrelevant);
// - |message_.fresh_id| must be a fresh id
// - |message_.type_id| must be the id of a composite type
@@ -42,9 +42,11 @@
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
- // Adds an OpConstantComposite instruction defining a constant of type
- // |message_.type_id|, using |message_.constituent_id| as constituents, with
- // result id |message_.fresh_id|.
+ // - Adds an OpConstantComposite instruction defining a constant of type
+ // |message_.type_id|, using |message_.constituent_id| as constituents, with
+ // result id |message_.fresh_id|.
+ // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is
+ // true.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
index e13d08f..98bfbf0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.cpp
@@ -24,9 +24,11 @@
: message_(message) {}
TransformationAddConstantScalar::TransformationAddConstantScalar(
- uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
+ uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words,
+ bool is_irrelevant) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
+ message_.set_is_irrelevant(is_irrelevant);
for (auto word : words) {
message_.add_word(word);
}
@@ -60,7 +62,8 @@
}
void TransformationAddConstantScalar::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
opt::Instruction::OperandList operand_list;
for (auto word : message_.word()) {
operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
@@ -75,6 +78,11 @@
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
+
+ if (message_.is_irrelevant()) {
+ transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+ message_.fresh_id());
+ }
}
protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
index e0ed39f..06a77fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_constant_scalar.h
@@ -31,7 +31,8 @@
const protobufs::TransformationAddConstantScalar& message);
TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
- std::vector<uint32_t> words);
+ const std::vector<uint32_t>& words,
+ bool is_irrelevant);
// - |message_.fresh_id| must not be used by the module
// - |message_.type_id| must be the id of a floating-point or integer type
@@ -42,6 +43,7 @@
const TransformationContext& transformation_context) const override;
// Adds a new OpConstant instruction with the given type and words.
+ // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp
new file mode 100644
index 0000000..e9c401d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.cpp
@@ -0,0 +1,193 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+ const protobufs::TransformationAddCopyMemory& message)
+ : message_(message) {}
+
+TransformationAddCopyMemory::TransformationAddCopyMemory(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+ uint32_t initializer_id) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+ message_.set_fresh_id(fresh_id);
+ message_.set_source_id(source_id);
+ message_.set_storage_class(storage_class);
+ message_.set_initializer_id(initializer_id);
+}
+
+bool TransformationAddCopyMemory::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // Check that target id is fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ return false;
+ }
+
+ // Check that instruction descriptor is valid. This also checks that
+ // |message_.instruction_descriptor| is not a global instruction.
+ auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context);
+ if (!inst) {
+ return false;
+ }
+
+ // Check that we can insert OpCopyMemory before |instruction_descriptor|.
+ auto iter = fuzzerutil::GetIteratorForInstruction(
+ ir_context->get_instr_block(inst), inst);
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) {
+ return false;
+ }
+
+ // Check that source instruction exists and is valid.
+ auto* source_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.source_id());
+ if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) {
+ return false;
+ }
+
+ // |storage_class| is either Function or Private.
+ if (message_.storage_class() != SpvStorageClassFunction &&
+ message_.storage_class() != SpvStorageClassPrivate) {
+ return false;
+ }
+
+ auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, source_inst->type_id());
+
+ // OpTypePointer with |message_.storage_class| exists.
+ if (!fuzzerutil::MaybeGetPointerType(
+ ir_context, pointee_type_id,
+ static_cast<SpvStorageClass>(message_.storage_class()))) {
+ return false;
+ }
+
+ // Check that |initializer_id| exists and has valid type.
+ const auto* initializer_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+ if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) {
+ return false;
+ }
+
+ // Check that domination rules are satisfied.
+ return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst,
+ message_.source_id());
+}
+
+void TransformationAddCopyMemory::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Insert OpCopyMemory before |instruction_descriptor|.
+ auto* insert_before_inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ assert(insert_before_inst);
+
+ auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
+ ir_context->get_instr_block(insert_before_inst), insert_before_inst);
+
+ insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCopyMemory, 0, 0,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}));
+
+ // Add global or local variable to copy memory into.
+ auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
+ auto type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context,
+ fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())),
+ storage_class);
+
+ if (storage_class == SpvStorageClassPrivate) {
+ fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
+ storage_class, message_.initializer_id());
+ } else {
+ assert(storage_class == SpvStorageClassFunction &&
+ "Storage class can be either Private or Function");
+ fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id,
+ ir_context->get_instr_block(insert_before_inst)
+ ->GetParent()
+ ->result_id(),
+ message_.initializer_id());
+ }
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ // Even though the copy memory instruction will - at least temporarily - lead
+ // to the destination and source pointers referring to identical values, this
+ // fact is not guaranteed to hold throughout execution of the SPIR-V code
+ // since the source pointer could be over-written. We thus assume nothing
+ // about the destination pointer, and record this fact so that the destination
+ // pointer can be used freely by other fuzzer passes.
+ transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+ message_.fresh_id());
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddCopyMemory::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_copy_memory() = message_;
+ return result;
+}
+
+bool TransformationAddCopyMemory::IsInstructionSupported(
+ opt::IRContext* ir_context, opt::Instruction* inst) {
+ if (!inst->result_id() || !inst->type_id() ||
+ inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) {
+ return false;
+ }
+
+ const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+ assert(type && "Instruction must have a valid type");
+
+ return type->AsPointer() &&
+ CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type());
+}
+
+bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory(
+ const opt::analysis::Type& type) {
+ switch (type.kind()) {
+ case opt::analysis::Type::kBool:
+ case opt::analysis::Type::kInteger:
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kArray:
+ return true;
+ case opt::analysis::Type::kVector:
+ return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type());
+ case opt::analysis::Type::kMatrix:
+ return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type());
+ case opt::analysis::Type::kStruct:
+ return std::all_of(type.AsStruct()->element_types().begin(),
+ type.AsStruct()->element_types().end(),
+ [](const opt::analysis::Type* element) {
+ return CanUsePointeeWithCopyMemory(*element);
+ });
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h
new file mode 100644
index 0000000..138d992
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_copy_memory.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddCopyMemory : public Transformation {
+ public:
+ explicit TransformationAddCopyMemory(
+ const protobufs::TransformationAddCopyMemory& message);
+
+ TransformationAddCopyMemory(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class,
+ uint32_t initializer_id);
+
+ // - |instruction_descriptor| must point to a valid instruction in the module.
+ // - it should be possible to insert OpCopyMemory before
+ // |instruction_descriptor| (i.e. the module remains valid after the
+ // insertion).
+ // - |source_id| must be a result id for some valid instruction in the module.
+ // - |fresh_id| must be a fresh id to copy memory into.
+ // - type of |source_id| must be OpTypePointer where pointee can be used with
+ // OpCopyMemory.
+ // - |storage_class| must be either Private or Function.
+ // - type ids of instructions with result ids |source_id| and |initialize_id|
+ // must be the same.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // A global or local variable with id |target_id| and |storage_class| class is
+ // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted
+ // before the |instruction_descriptor|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if we can copy memory from |instruction| using OpCopyMemory.
+ static bool IsInstructionSupported(opt::IRContext* ir_context,
+ opt::Instruction* inst);
+
+ private:
+ // Returns whether the type, pointed to by some OpTypePointer, can be used
+ // with OpCopyMemory instruction.
+ static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type);
+
+ protobufs::TransformationAddCopyMemory message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
index b246c3f..5b06e97 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_block.cpp
@@ -32,7 +32,8 @@
}
bool TransformationAddDeadBlock::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
// The new block's id must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
@@ -40,8 +41,8 @@
// First, we check that a constant with the same value as
// |message_.condition_value| is present.
- if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
- message_.condition_value())) {
+ if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+ message_.condition_value(), false)) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
@@ -92,8 +93,8 @@
existing_block->terminator()->GetSingleWordInOperand(0);
// Get the id of the boolean value that will be used as the branch condition.
- auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context,
- message_.condition_value());
+ auto bool_id = fuzzerutil::MaybeGetBoolConstant(
+ ir_context, *transformation_context, message_.condition_value(), false);
// Make a new block that unconditionally branches to the original successor
// block.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
index db9de7d..284174a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.cpp
@@ -85,12 +85,10 @@
// loop as part of the loop, but it is not legal to jump from a loop's
// continue construct to the loop's merge (except from the back-edge block),
// so we need to check for this case.
- //
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not
- // currently allow a dead break from a back edge block, but we could and
- // ultimately should.
return !fuzzerutil::BlockIsInLoopContinueConstruct(
- ir_context, message_.from_block(), containing_construct);
+ ir_context, message_.from_block(), containing_construct) ||
+ fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+ containing_construct);
}
// Case (3) holds if and only if |to_block| is the merge block for this
@@ -102,7 +100,9 @@
message_.to_block() ==
ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
return !fuzzerutil::BlockIsInLoopContinueConstruct(
- ir_context, message_.from_block(), containing_loop_header);
+ ir_context, message_.from_block(), containing_loop_header) ||
+ fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+ containing_loop_header);
}
return false;
}
@@ -112,8 +112,9 @@
const TransformationContext& transformation_context) const {
// First, we check that a constant with the same value as
// |message_.break_condition_value| is present.
- if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
- message_.break_condition_value())) {
+ if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+ message_.break_condition_value(),
+ false)) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
@@ -179,14 +180,15 @@
// the validator is complete with respect to checking structured control flow
// rules.
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
- ApplyImpl(cloned_context.get());
+ ApplyImpl(cloned_context.get(), transformation_context);
return fuzzerutil::IsValid(cloned_context.get(),
transformation_context.GetValidatorOptions());
}
void TransformationAddDeadBreak::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- ApplyImpl(ir_context);
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ ApplyImpl(ir_context, *transformation_context);
// Invalidate all analyses
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
@@ -199,11 +201,14 @@
}
void TransformationAddDeadBreak::ApplyImpl(
- spvtools::opt::IRContext* ir_context) const {
+ spvtools::opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
ir_context, ir_context->cfg()->block(message_.from_block()),
ir_context->cfg()->block(message_.to_block()),
- message_.break_condition_value(), message_.phi_id());
+ fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+ message_.break_condition_value(), false),
+ message_.phi_id());
}
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
index 0ea9210..f010b11 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_break.h
@@ -75,7 +75,8 @@
// module. This is only invoked by 'IsApplicable' after certain basic
// applicability checks have been made, ensuring that the invocation of this
// method is legal.
- void ApplyImpl(opt::IRContext* ir_context) const;
+ void ApplyImpl(opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const;
protobufs::TransformationAddDeadBreak message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
index 1fc6d67..b5b7ae3 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
@@ -38,8 +38,9 @@
const TransformationContext& transformation_context) const {
// First, we check that a constant with the same value as
// |message_.continue_condition_value| is present.
- if (!fuzzerutil::MaybeGetBoolConstantId(
- ir_context, message_.continue_condition_value())) {
+ if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+ message_.continue_condition_value(),
+ false)) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
@@ -119,14 +120,15 @@
// the validator is complete with respect to checking structured control flow
// rules.
auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
- ApplyImpl(cloned_context.get());
+ ApplyImpl(cloned_context.get(), transformation_context);
return fuzzerutil::IsValid(cloned_context.get(),
transformation_context.GetValidatorOptions());
}
void TransformationAddDeadContinue::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- ApplyImpl(ir_context);
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ ApplyImpl(ir_context, *transformation_context);
// Invalidate all analyses
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
@@ -139,7 +141,8 @@
}
void TransformationAddDeadContinue::ApplyImpl(
- spvtools::opt::IRContext* ir_context) const {
+ spvtools::opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
auto bb_from = ir_context->cfg()->block(message_.from_block());
auto continue_block =
bb_from->IsLoopHeader()
@@ -149,7 +152,10 @@
assert(continue_block && "message_.from_block must be in a loop.");
fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
ir_context, bb_from, ir_context->cfg()->block(continue_block),
- message_.continue_condition_value(), message_.phi_id());
+ fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+ message_.continue_condition_value(),
+ false),
+ message_.phi_id());
}
} // namespace fuzz
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
index 1053c16..b977bb2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.h
@@ -72,7 +72,8 @@
// module. This is only invoked by 'IsApplicable' after certain basic
// applicability checks have been made, ensuring that the invocation of this
// method is legal.
- void ApplyImpl(opt::IRContext* ir_context) const;
+ void ApplyImpl(opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const;
protobufs::TransformationAddDeadContinue message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
index 90276ed..380d59c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.cpp
@@ -794,8 +794,8 @@
// Get the bound for the composite being indexed into; e.g. the number of
// columns of matrix or the size of an array.
- uint32_t bound =
- GetBoundForCompositeIndex(ir_context, *should_be_composite_type);
+ uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+ *should_be_composite_type, ir_context);
// Get the instruction associated with the index and figure out its integer
// type.
@@ -873,28 +873,6 @@
return true;
}
-uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
- opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
- switch (composite_type_inst.opcode()) {
- case SpvOpTypeArray:
- return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
- case SpvOpTypeMatrix:
- case SpvOpTypeVector:
- return composite_type_inst.GetSingleWordInOperand(1);
- case SpvOpTypeStruct: {
- return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
- }
- case SpvOpTypeRuntimeArray:
- assert(false &&
- "GetBoundForCompositeIndex should not be invoked with an "
- "OpTypeRuntimeArray, which does not have a static bound.");
- return 0;
- default:
- assert(false && "Unknown composite type.");
- return 0;
- }
-}
-
opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
uint32_t index_id) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
index 5af197b..4a84c70 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_function.h
@@ -58,13 +58,6 @@
protobufs::Transformation ToMessage() const override;
- // Helper method that returns the bound for indexing into a composite of type
- // |composite_type_inst|, i.e. the number of fields of a struct, the size of
- // an array, the number of components of a vector, or the number of columns of
- // a matrix.
- static uint32_t GetBoundForCompositeIndex(
- opt::IRContext* ir_context, const opt::Instruction& composite_type_inst);
-
// Helper method that, given composite type |composite_type_inst|, returns the
// type of the sub-object at index |index_id|, which is required to be in-
// bounds.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
index 6464bfb..303c4d9 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.cpp
@@ -93,32 +93,10 @@
void TransformationAddGlobalVariable::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
- opt::Instruction::OperandList input_operands;
- input_operands.push_back(
- {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
- if (message_.initializer_id()) {
- input_operands.push_back(
- {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
- }
- ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
- ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
- input_operands));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-
- if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(ir_context)) {
- // Conservatively add this global to the interface of every entry point in
- // the module. This means that the global is available for other
- // transformations to use.
- //
- // A downside of this is that the global will be in the interface even if it
- // ends up never being used.
- //
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
- // this if a more thorough approach to entry point interfaces is taken.
- for (auto& entry_point : ir_context->module()->entry_points()) {
- entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
- }
- }
+ fuzzerutil::AddGlobalVariable(
+ ir_context, message_.fresh_id(), message_.type_id(),
+ static_cast<SpvStorageClass>(message_.storage_class()),
+ message_.initializer_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -137,22 +115,5 @@
return result;
}
-bool TransformationAddGlobalVariable::
- GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
- opt::IRContext* ir_context) {
- // TODO(afd): We capture the universal environments for which this requirement
- // holds. The check should be refined on demand for other target
- // environments.
- switch (ir_context->grammar().target_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:
- return false;
- default:
- return true;
- }
-}
-
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
index 289af9e..89bd044 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_global_variable.h
@@ -58,12 +58,6 @@
protobufs::Transformation ToMessage() const override;
private:
- // Returns true if and only if the SPIR-V version being used requires that
- // global variables accessed in the static call graph of an entry point need
- // to be listed in that entry point's interface.
- static bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
- opt::IRContext* ir_context);
-
protobufs::TransformationAddGlobalVariable message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..1be1d43
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddImageSampleUnusedComponents::
+ TransformationAddImageSampleUnusedComponents(
+ const spvtools::fuzz::protobufs::
+ TransformationAddImageSampleUnusedComponents& message)
+ : message_(message) {}
+
+TransformationAddImageSampleUnusedComponents::
+ TransformationAddImageSampleUnusedComponents(
+ uint32_t coordinate_with_unused_components_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ message_.set_coordinate_with_unused_components_id(
+ coordinate_with_unused_components_id);
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationAddImageSampleUnusedComponents::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ auto image_sample_instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+
+ // The image sample instruction must be defined.
+ if (image_sample_instruction == nullptr) {
+ return false;
+ }
+
+ // The instruction must be an image sample instruction.
+ if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) {
+ return false;
+ }
+
+ uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1);
+ auto coordinate_instruction =
+ ir_context->get_def_use_mgr()->GetDef(coordinate_id);
+ auto coordinate_type =
+ ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id());
+
+ // It must be possible to add unused components.
+ if (coordinate_type->AsVector() &&
+ coordinate_type->AsVector()->element_count() == 4) {
+ return false;
+ }
+
+ auto coordinate_with_unused_components_instruction =
+ ir_context->get_def_use_mgr()->GetDef(
+ message_.coordinate_with_unused_components_id());
+
+ // The coordinate with unused components instruction must be defined.
+ if (coordinate_with_unused_components_instruction == nullptr) {
+ return false;
+ }
+
+ // It must be an OpCompositeConstruct instruction such that it can be checked
+ // that the original components are present.
+ if (coordinate_with_unused_components_instruction->opcode() !=
+ SpvOpCompositeConstruct) {
+ return false;
+ }
+
+ // The first constituent must be the original coordinate.
+ if (coordinate_with_unused_components_instruction->GetSingleWordInOperand(
+ 0) != coordinate_id) {
+ return false;
+ }
+
+ auto coordinate_with_unused_components_type =
+ ir_context->get_type_mgr()->GetType(
+ coordinate_with_unused_components_instruction->type_id());
+
+ // |coordinate_with_unused_components_type| must be a vector.
+ if (!coordinate_with_unused_components_type->AsVector()) {
+ return false;
+ }
+
+ return true;
+}
+
+void TransformationAddImageSampleUnusedComponents::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ // Sets the coordinate operand.
+ auto image_sample_instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ image_sample_instruction->SetInOperand(
+ 1, {message_.coordinate_with_unused_components_id()});
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationAddImageSampleUnusedComponents::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_image_sample_unused_components() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h
new file mode 100644
index 0000000..7493481
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_image_sample_unused_components.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddImageSampleUnusedComponents : public Transformation {
+ public:
+ explicit TransformationAddImageSampleUnusedComponents(
+ const protobufs::TransformationAddImageSampleUnusedComponents& message);
+
+ TransformationAddImageSampleUnusedComponents(
+ uint32_t coordinate_with_unused_components_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |coordinate_with_unused_components_id| must identify a vector such that
+ // the first components match the components of the image sample coordinate.
+ // - |message_.instruction_descriptor| must identify an image sample
+ // instruction
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Add unused components to an image sample coordinate by replacing the
+ // coordinate with |coordinate_with_unused_components_id|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddImageSampleUnusedComponents message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
index 5136249..a6b31b4 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_local_variable.cpp
@@ -70,18 +70,10 @@
void TransformationAddLocalVariable::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
- fuzzerutil::FindFunction(ir_context, message_.function_id())
- ->begin()
- ->begin()
- ->InsertBefore(MakeUnique<opt::Instruction>(
- ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
- opt::Instruction::OperandList(
- {{SPV_OPERAND_TYPE_STORAGE_CLASS,
- {
+ fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(),
+ message_.type_id(), message_.function_id(),
+ message_.initializer_id());
- SpvStorageClassFunction}},
- {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
message_.fresh_id());
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp
new file mode 100644
index 0000000..cc32362
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.cpp
@@ -0,0 +1,169 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_parameter.h"
+
+#include <source/spirv_constant.h>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddParameter::TransformationAddParameter(
+ const protobufs::TransformationAddParameter& message)
+ : message_(message) {}
+
+TransformationAddParameter::TransformationAddParameter(
+ uint32_t function_id, uint32_t parameter_fresh_id, uint32_t initializer_id,
+ uint32_t function_type_fresh_id) {
+ message_.set_function_id(function_id);
+ message_.set_parameter_fresh_id(parameter_fresh_id);
+ message_.set_initializer_id(initializer_id);
+ message_.set_function_type_fresh_id(function_type_fresh_id);
+}
+
+bool TransformationAddParameter::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // Check that function exists
+ const auto* function =
+ fuzzerutil::FindFunction(ir_context, message_.function_id());
+ if (!function ||
+ fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+ return false;
+ }
+
+ // Check that |initializer_id| is valid.
+ const auto* initializer_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+
+ if (!initializer_inst) {
+ return false;
+ }
+
+ // Check that initializer's type is valid.
+ const auto* initializer_type =
+ ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
+
+ if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) {
+ return false;
+ }
+
+ return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) &&
+ fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
+ message_.parameter_fresh_id() != message_.function_type_fresh_id();
+}
+
+void TransformationAddParameter::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Find the function that will be transformed
+ auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
+ assert(function && "Can't find the function");
+
+ auto parameter_type_id =
+ fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
+ assert(parameter_type_id != 0 && "Initializer has invalid type");
+
+ // Add new parameters to the function.
+ function->AddParameter(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFunctionParameter, parameter_type_id,
+ message_.parameter_fresh_id(), opt::Instruction::OperandList()));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
+
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+ // Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
+
+ // Mark new parameter as irrelevant so that we can replace its use with some
+ // other id.
+ transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+ message_.parameter_fresh_id());
+
+ // Fix all OpFunctionCall instructions.
+ ir_context->get_def_use_mgr()->ForEachUser(
+ &function->DefInst(), [this](opt::Instruction* call) {
+ if (call->opcode() != SpvOpFunctionCall ||
+ call->GetSingleWordInOperand(0) != message_.function_id()) {
+ return;
+ }
+
+ call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+ });
+
+ auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+ assert(old_function_type && "Function must have a valid type");
+
+ if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+ // Adjust existing function type if it is used only by this function.
+ old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}});
+
+ // We must make sure that all dependencies of |old_function_type| are
+ // evaluated before |old_function_type| (i.e. the domination rules are not
+ // broken). Thus, we move |old_function_type| to the end of the list of all
+ // types in the module.
+ old_function_type->RemoveFromList();
+ ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
+ } else {
+ // Otherwise, either create a new type or use an existing one.
+ std::vector<uint32_t> type_ids;
+ type_ids.reserve(old_function_type->NumInOperands() + 1);
+
+ for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) {
+ type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
+ }
+
+ type_ids.push_back(parameter_type_id);
+
+ function->DefInst().SetInOperand(
+ 1, {fuzzerutil::FindOrCreateFunctionType(
+ ir_context, message_.function_type_fresh_id(), type_ids)});
+ }
+
+ // Make sure our changes are analyzed.
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddParameter::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_parameter() = message_;
+ return result;
+}
+
+bool TransformationAddParameter::IsParameterTypeSupported(
+ const opt::analysis::Type& type) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+ // Think about other type instructions we can add here.
+ switch (type.kind()) {
+ case opt::analysis::Type::kBool:
+ case opt::analysis::Type::kInteger:
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kArray:
+ case opt::analysis::Type::kMatrix:
+ case opt::analysis::Type::kVector:
+ return true;
+ case opt::analysis::Type::kStruct:
+ return std::all_of(type.AsStruct()->element_types().begin(),
+ type.AsStruct()->element_types().end(),
+ [](const opt::analysis::Type* element_type) {
+ return IsParameterTypeSupported(*element_type);
+ });
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h
new file mode 100644
index 0000000..e6b9019
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_parameter.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddParameter : public Transformation {
+ public:
+ explicit TransformationAddParameter(
+ const protobufs::TransformationAddParameter& message);
+
+ TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id,
+ uint32_t initializer_id,
+ uint32_t function_type_fresh_id);
+
+ // - |function_id| must be a valid result id of some non-entry-point function
+ // in the module.
+ // - |initializer_id| must be a valid result id of some instruction in the
+ // module. Instruction's type must be supported by this transformation
+ // as specified by IsParameterTypeSupported function.
+ // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
+ // not equal.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // - Creates a new OpFunctionParameter instruction with result id
+ // |parameter_fresh_id| for the function with |function_id|.
+ // - Adjusts function's type to include a new parameter.
+ // - Adds |initializer_id| as a new operand to every OpFunctionCall
+ // instruction that calls the function.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if the type of the parameter is supported by this
+ // transformation.
+ static bool IsParameterTypeSupported(const opt::analysis::Type& type);
+
+ private:
+ protobufs::TransformationAddParameter message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp
new file mode 100644
index 0000000..effa71d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+ const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration&
+ message)
+ : message_(message) {}
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+ uint32_t result_id) {
+ message_.set_result_id(result_id);
+}
+
+bool TransformationAddRelaxedDecoration::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ // |message_.result_id| must be the id of an instruction.
+ auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ if (!instr) {
+ return false;
+ }
+ opt::BasicBlock* cur_block = ir_context->get_instr_block(instr);
+ // The instruction must have a block.
+ if (cur_block == nullptr) {
+ return false;
+ }
+ // |cur_block| must be a dead block.
+ if (!(transformation_context.GetFactManager()->BlockIsDead(
+ cur_block->id()))) {
+ return false;
+ }
+ // The instruction must be numeric.
+ return IsNumeric(instr->opcode());
+}
+
+void TransformationAddRelaxedDecoration::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ // Add a RelaxedPrecision decoration targeting |message_.result_id|.
+ ir_context->get_decoration_mgr()->AddDecoration(
+ message_.result_id(), SpvDecorationRelaxedPrecision);
+}
+
+protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_add_relaxed_decoration() = message_;
+ return result;
+}
+
+bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) {
+ switch (opcode) {
+ case SpvOpConvertFToU:
+ case SpvOpConvertFToS:
+ case SpvOpConvertSToF:
+ case SpvOpConvertUToF:
+ case SpvOpUConvert:
+ case SpvOpSConvert:
+ case SpvOpFConvert:
+ case SpvOpConvertPtrToU:
+ case SpvOpSatConvertSToU:
+ case SpvOpSatConvertUToS:
+ case SpvOpVectorExtractDynamic:
+ case SpvOpVectorInsertDynamic:
+ case SpvOpVectorShuffle:
+ case SpvOpTranspose:
+ case SpvOpSNegate:
+ case SpvOpFNegate:
+ case SpvOpIAdd:
+ case SpvOpFAdd:
+ case SpvOpISub:
+ case SpvOpFSub:
+ case SpvOpIMul:
+ case SpvOpFMul:
+ case SpvOpUDiv:
+ case SpvOpSDiv:
+ case SpvOpFDiv:
+ case SpvOpUMod:
+ case SpvOpSRem:
+ case SpvOpSMod:
+ case SpvOpFRem:
+ case SpvOpFMod:
+ case SpvOpVectorTimesScalar:
+ case SpvOpMatrixTimesScalar:
+ case SpvOpVectorTimesMatrix:
+ case SpvOpMatrixTimesVector:
+ case SpvOpMatrixTimesMatrix:
+ case SpvOpOuterProduct:
+ case SpvOpDot:
+ case SpvOpIAddCarry:
+ case SpvOpISubBorrow:
+ case SpvOpUMulExtended:
+ case SpvOpSMulExtended:
+ case SpvOpShiftRightLogical:
+ case SpvOpShiftRightArithmetic:
+ case SpvOpShiftLeftLogical:
+ case SpvOpBitwiseOr:
+ case SpvOpBitwiseXor:
+ case SpvOpBitwiseAnd:
+ case SpvOpNot:
+ case SpvOpBitFieldInsert:
+ case SpvOpBitFieldSExtract:
+ case SpvOpBitFieldUExtract:
+ case SpvOpBitReverse:
+ case SpvOpBitCount:
+ case SpvOpAtomicLoad:
+ case SpvOpAtomicStore:
+ case SpvOpAtomicExchange:
+ case SpvOpAtomicCompareExchange:
+ case SpvOpAtomicCompareExchangeWeak:
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicSMin:
+ case SpvOpAtomicUMin:
+ case SpvOpAtomicSMax:
+ case SpvOpAtomicUMax:
+ case SpvOpAtomicAnd:
+ case SpvOpAtomicOr:
+ case SpvOpAtomicXor:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.h
new file mode 100644
index 0000000..30c1abf
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_relaxed_decoration.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
+#define SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddRelaxedDecoration : public Transformation {
+ public:
+ explicit TransformationAddRelaxedDecoration(
+ const protobufs::TransformationAddRelaxedDecoration& message);
+
+ explicit TransformationAddRelaxedDecoration(uint32_t fresh_id);
+
+ // - |message_.result_id| must be the result id of an instruction, which is
+ // located in a dead block and Relaxed decoration can be applied.
+ // - It does not matter whether this instruction is already annotated with the
+ // Relaxed decoration.
+ bool IsApplicable(
+
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Adds a decoration of the form:
+ // 'OpDecoration |message_.result_id| RelaxedPrecision'
+ // to the module.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if and only if |opcode| is the opcode of an instruction
+ // that operates on 32-bit integers and 32-bit floats
+ // as defined by the SPIR-V specification.
+ static bool IsNumeric(uint32_t opcode);
+
+ private:
+ protobufs::TransformationAddRelaxedDecoration message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp
new file mode 100644
index 0000000..d6a7083
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.cpp
@@ -0,0 +1,84 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_spec_constant_op.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
+ spvtools::fuzz::protobufs::TransformationAddSpecConstantOp message)
+ : message_(std::move(message)) {}
+
+TransformationAddSpecConstantOp::TransformationAddSpecConstantOp(
+ uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+ const opt::Instruction::OperandList& operands) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_type_id(type_id);
+ message_.set_opcode(opcode);
+ for (const auto& operand : operands) {
+ auto* op = message_.add_operand();
+ op->set_operand_type(operand.type);
+ for (auto word : operand.words) {
+ op->add_operand_data(word);
+ }
+ }
+}
+
+bool TransformationAddSpecConstantOp::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ auto clone = fuzzerutil::CloneIRContext(ir_context);
+ ApplyImpl(clone.get());
+ return fuzzerutil::IsValid(clone.get(),
+ transformation_context.GetValidatorOptions());
+}
+
+void TransformationAddSpecConstantOp::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ ApplyImpl(ir_context);
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+void TransformationAddSpecConstantOp::ApplyImpl(
+ opt::IRContext* ir_context) const {
+ opt::Instruction::OperandList operands = {
+ {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {message_.opcode()}}};
+
+ for (const auto& operand : message_.operand()) {
+ std::vector<uint32_t> words(operand.operand_data().begin(),
+ operand.operand_data().end());
+ operands.push_back({static_cast<spv_operand_type_t>(operand.operand_type()),
+ std::move(words)});
+ }
+
+ ir_context->AddGlobalValue(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpSpecConstantOp, message_.type_id(), message_.fresh_id(),
+ std::move(operands)));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+}
+
+protobufs::Transformation TransformationAddSpecConstantOp::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_spec_constant_op() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h
new file mode 100644
index 0000000..c0f7154
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_spec_constant_op.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddSpecConstantOp : public Transformation {
+ public:
+ explicit TransformationAddSpecConstantOp(
+ protobufs::TransformationAddSpecConstantOp message);
+
+ TransformationAddSpecConstantOp(
+ uint32_t fresh_id, uint32_t type_id, SpvOp opcode,
+ const opt::Instruction::OperandList& operands);
+
+ // - |fresh_id| is a fresh result id in the module.
+ // - |type_id| is a valid result id of some OpType* instruction in the
+ // module. It is also a valid type id with respect to |opcode|.
+ // - |opcode| is one of the opcodes supported by OpSpecConstantOp.
+ // - |operands| are valid with respect to |opcode|
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // |%fresh_id = OpSpecConstantOp %type_id opcode operands...| is added to the
+ // module.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ void ApplyImpl(opt::IRContext* ir_context) const;
+
+ protobufs::TransformationAddSpecConstantOp message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp
new file mode 100644
index 0000000..6a93e61
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.cpp
@@ -0,0 +1,313 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_synonym.h"
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddSynonym::TransformationAddSynonym(
+ protobufs::TransformationAddSynonym message)
+ : message_(std::move(message)) {}
+
+TransformationAddSynonym::TransformationAddSynonym(
+ uint32_t result_id,
+ protobufs::TransformationAddSynonym::SynonymType synonym_type,
+ uint32_t synonym_fresh_id,
+ const protobufs::InstructionDescriptor& insert_before) {
+ message_.set_result_id(result_id);
+ message_.set_synonym_type(synonym_type);
+ message_.set_synonym_fresh_id(synonym_fresh_id);
+ *message_.mutable_insert_before() = insert_before;
+}
+
+bool TransformationAddSynonym::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
+ message_.synonym_type()) &&
+ "Synonym type is invalid");
+
+ // |synonym_fresh_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
+ return false;
+ }
+
+ // Check that |message_.result_id| is valid.
+ auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ if (!synonym) {
+ return false;
+ }
+
+ // Check that we can apply |synonym_type| to |result_id|.
+ if (!IsInstructionValid(ir_context, transformation_context, synonym,
+ message_.synonym_type())) {
+ return false;
+ }
+
+ // Check that |insert_before| is valid.
+ auto* insert_before_inst =
+ FindInstruction(message_.insert_before(), ir_context);
+ if (!insert_before_inst) {
+ return false;
+ }
+
+ // Check that we can insert |message._synonymous_instruction| before
+ // |message_.insert_before| instruction. We use OpIAdd to represent some
+ // instruction that can produce a synonym.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd,
+ insert_before_inst)) {
+ return false;
+ }
+
+ // A constant instruction must be present in the module if required.
+ if (IsAdditionalConstantRequired(message_.synonym_type()) &&
+ MaybeGetConstantId(ir_context, transformation_context) == 0) {
+ return false;
+ }
+
+ // Domination rules must be satisfied.
+ return fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, insert_before_inst, message_.result_id());
+}
+
+void TransformationAddSynonym::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Add a synonymous instruction.
+ FindInstruction(message_.insert_before(), ir_context)
+ ->InsertBefore(
+ MakeSynonymousInstruction(ir_context, *transformation_context));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
+
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+
+ // Propagate PointeeValueIsIrrelevant fact.
+ const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
+ fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
+ assert(new_synonym_type && "New synonym should have a valid type");
+
+ if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+ message_.result_id()) &&
+ new_synonym_type->AsPointer()) {
+ transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+ message_.synonym_fresh_id());
+ }
+
+ // Mark two ids as synonymous.
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(message_.result_id(), {}),
+ MakeDataDescriptor(message_.synonym_fresh_id(), {}), ir_context);
+}
+
+protobufs::Transformation TransformationAddSynonym::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_synonym() = message_;
+ return result;
+}
+
+bool TransformationAddSynonym::IsInstructionValid(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context, opt::Instruction* inst,
+ protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+ // Instruction must have a result id, type id. We skip OpUndef and
+ // OpConstantNull.
+ if (!inst || !inst->result_id() || !inst->type_id() ||
+ inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
+ return false;
+ }
+
+ if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
+ return false;
+ }
+
+ switch (synonym_type) {
+ case protobufs::TransformationAddSynonym::ADD_ZERO:
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ case protobufs::TransformationAddSynonym::MUL_ONE: {
+ // The instruction must be either scalar or vector of integers or floats.
+ const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+ assert(type && "Instruction's result id is invalid");
+
+ if (const auto* vector = type->AsVector()) {
+ return vector->element_type()->AsInteger() ||
+ vector->element_type()->AsFloat();
+ }
+
+ return type->AsInteger() || type->AsFloat();
+ }
+ case protobufs::TransformationAddSynonym::COPY_OBJECT:
+ // All checks for OpCopyObject are handled by
+ // fuzzerutil::CanMakeSynonymOf.
+ return true;
+ case protobufs::TransformationAddSynonym::LOGICAL_AND:
+ case protobufs::TransformationAddSynonym::LOGICAL_OR: {
+ // The instruction must be either a scalar or a vector of booleans.
+ const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+ assert(type && "Instruction's result id is invalid");
+ return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
+ type->AsBool();
+ }
+ default:
+ assert(false && "Synonym type is not supported");
+ return false;
+ }
+}
+
+std::unique_ptr<opt::Instruction>
+TransformationAddSynonym::MakeSynonymousInstruction(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ auto synonym_type_id =
+ fuzzerutil::GetTypeId(ir_context, message_.result_id());
+ assert(synonym_type_id && "Synonym has invalid type id");
+
+ switch (message_.synonym_type()) {
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ case protobufs::TransformationAddSynonym::MUL_ONE:
+ case protobufs::TransformationAddSynonym::ADD_ZERO: {
+ const auto* synonym_type =
+ ir_context->get_type_mgr()->GetType(synonym_type_id);
+ assert(synonym_type && "Synonym has invalid type");
+
+ // Compute instruction's opcode based on the type of the operand.
+ // We have already checked that the operand is either a scalar or a vector
+ // of either integers or floats.
+ auto is_integral =
+ (synonym_type->AsVector() &&
+ synonym_type->AsVector()->element_type()->AsInteger()) ||
+ synonym_type->AsInteger();
+ auto opcode = SpvOpNop;
+ switch (message_.synonym_type()) {
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ opcode = is_integral ? SpvOpISub : SpvOpFSub;
+ break;
+ case protobufs::TransformationAddSynonym::MUL_ONE:
+ opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+ break;
+ case protobufs::TransformationAddSynonym::ADD_ZERO:
+ opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+ break;
+ default:
+ assert(false && "Unreachable");
+ break;
+ }
+
+ return MakeUnique<opt::Instruction>(
+ ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+ {SPV_OPERAND_TYPE_ID,
+ {MaybeGetConstantId(ir_context, transformation_context)}}});
+ }
+ case protobufs::TransformationAddSynonym::COPY_OBJECT:
+ return MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCopyObject, synonym_type_id,
+ message_.synonym_fresh_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
+ case protobufs::TransformationAddSynonym::LOGICAL_OR:
+ case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+ auto opcode = message_.synonym_type() ==
+ protobufs::TransformationAddSynonym::LOGICAL_OR
+ ? SpvOpLogicalOr
+ : SpvOpLogicalAnd;
+ return MakeUnique<opt::Instruction>(
+ ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+ {SPV_OPERAND_TYPE_ID,
+ {MaybeGetConstantId(ir_context, transformation_context)}}});
+ }
+ default:
+ assert(false && "Unhandled synonym type");
+ return nullptr;
+ }
+}
+
+uint32_t TransformationAddSynonym::MaybeGetConstantId(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
+ "Synonym type doesn't require an additional constant");
+
+ auto synonym_type_id =
+ fuzzerutil::GetTypeId(ir_context, message_.result_id());
+ assert(synonym_type_id && "Synonym has invalid type id");
+
+ switch (message_.synonym_type()) {
+ case protobufs::TransformationAddSynonym::ADD_ZERO:
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ case protobufs::TransformationAddSynonym::LOGICAL_OR:
+ return fuzzerutil::MaybeGetZeroConstant(
+ ir_context, transformation_context, synonym_type_id, false);
+ case protobufs::TransformationAddSynonym::MUL_ONE:
+ case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+ auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
+ assert(synonym_type && "Synonym has invalid type");
+
+ if (const auto* vector = synonym_type->AsVector()) {
+ auto element_type_id =
+ ir_context->get_type_mgr()->GetId(vector->element_type());
+ assert(element_type_id && "Vector's element type is invalid");
+
+ auto one_word =
+ vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
+ if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
+ ir_context, transformation_context, {one_word}, element_type_id,
+ false)) {
+ return fuzzerutil::MaybeGetCompositeConstant(
+ ir_context, transformation_context,
+ std::vector<uint32_t>(vector->element_count(), scalar_one_id),
+ synonym_type_id, false);
+ }
+
+ return 0;
+ } else {
+ return fuzzerutil::MaybeGetScalarConstant(
+ ir_context, transformation_context,
+ {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
+ synonym_type_id, false);
+ }
+ }
+ default:
+ // The assertion at the beginning of the function will fail in the debug
+ // mode.
+ return 0;
+ }
+}
+
+bool TransformationAddSynonym::IsAdditionalConstantRequired(
+ protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+ switch (synonym_type) {
+ case protobufs::TransformationAddSynonym::ADD_ZERO:
+ case protobufs::TransformationAddSynonym::SUB_ZERO:
+ case protobufs::TransformationAddSynonym::LOGICAL_OR:
+ case protobufs::TransformationAddSynonym::MUL_ONE:
+ case protobufs::TransformationAddSynonym::LOGICAL_AND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h
new file mode 100644
index 0000000..7705415
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_synonym.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddSynonym : public Transformation {
+ public:
+ explicit TransformationAddSynonym(
+ protobufs::TransformationAddSynonym message);
+
+ TransformationAddSynonym(
+ uint32_t result_id,
+ protobufs::TransformationAddSynonym::SynonymType synonym_type,
+ uint32_t synonym_fresh_id,
+ const protobufs::InstructionDescriptor& insert_before);
+
+ // - |result_id| must be a valid result id of some instruction in the module.
+ // - |result_id| may not be an irrelevant id.
+ // - |synonym_type| is a type of the synonymous instruction that will be
+ // created.
+ // - |synonym_fresh_id| is a fresh id.
+ // - |insert_before| must be a valid instruction descriptor and we must be
+ // able to insert a new synonymous instruction before |insert_before|.
+ // - |result_id| must be available before |insert_before|.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Creates a new synonymous instruction according to the |synonym_type| with
+ // result id |synonym_fresh_id|.
+ // Inserts that instruction before |insert_before| and creates a fact
+ // that the |synonym_fresh_id| and the |result_id| are synonymous.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if we can create a synonym of |inst| according to the
+ // |synonym_type|.
+ static bool IsInstructionValid(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context,
+ opt::Instruction* inst,
+ protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+ // Returns true if |synonym_type| requires an additional constant instruction
+ // to be present in the module.
+ static bool IsAdditionalConstantRequired(
+ protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+ private:
+ // Returns a new instruction which is synonymous to |message_.result_id|.
+ std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const;
+
+ // Returns a result id of a constant instruction that is required to be
+ // present in some synonym types (e.g. returns a result id of a zero constant
+ // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in
+ // the module. This method should only be called when
+ // IsAdditionalConstantRequired returns true.
+ uint32_t MaybeGetConstantId(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const;
+
+ protobufs::TransformationAddSynonym message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
index 80716e1..c0c434b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_float.cpp
@@ -38,17 +38,12 @@
// Applicable if there is no float type with this width already declared in
// the module.
- opt::analysis::Float float_type(message_.width());
- return ir_context->get_type_mgr()->GetId(&float_type) == 0;
+ return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0;
}
void TransformationAddTypeFloat::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- opt::Instruction::OperandList width = {
- {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}};
- ir_context->module()->AddType(MakeUnique<opt::Instruction>(
- ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
index 991a28b..c878025 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.cpp
@@ -55,48 +55,19 @@
// exactly the same return and argument type ids. (Note that the type manager
// does not allow us to check this, as it does not distinguish between
// function types with different but isomorphic pointer argument types.)
- for (auto& inst : ir_context->module()->types_values()) {
- if (inst.opcode() != SpvOpTypeFunction) {
- // Consider only OpTypeFunction instructions.
- continue;
- }
- if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) {
- // Different return types - cannot be the same.
- continue;
- }
- if (inst.NumInOperands() !=
- 1 + static_cast<uint32_t>(message_.argument_type_id().size())) {
- // Different numbers of arguments - cannot be the same.
- continue;
- }
- bool found_argument_mismatch = false;
- for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
- if (message_.argument_type_id(index - 1) !=
- inst.GetSingleWordInOperand(index)) {
- // Argument mismatch - cannot be the same.
- found_argument_mismatch = true;
- break;
- }
- }
- if (found_argument_mismatch) {
- continue;
- }
- // Everything matches - the type is already declared.
- return false;
- }
- return true;
+ std::vector<uint32_t> type_ids = {message_.return_type_id()};
+ type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
+ message_.argument_type_id().end());
+ return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0;
}
void TransformationAddTypeFunction::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- opt::Instruction::OperandList in_operands;
- in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}});
- for (auto argument_type_id : message_.argument_type_id()) {
- in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}});
- }
- ir_context->module()->AddType(MakeUnique<opt::Instruction>(
- ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ std::vector<uint32_t> type_ids = {message_.return_type_id()};
+ type_ids.insert(type_ids.end(), message_.argument_type_id().begin(),
+ message_.argument_type_id().end());
+
+ fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids);
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
index a932a5f..20759fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_int.cpp
@@ -40,18 +40,14 @@
// Applicable if there is no int type with this width and signedness already
// declared in the module.
- opt::analysis::Integer int_type(message_.width(), message_.is_signed());
- return ir_context->get_type_mgr()->GetId(&int_type) == 0;
+ return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(),
+ message_.is_signed()) == 0;
}
void TransformationAddTypeInt::Apply(opt::IRContext* ir_context,
TransformationContext* /*unused*/) const {
- opt::Instruction::OperandList in_operands = {
- {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}},
- {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}};
- ir_context->module()->AddType(MakeUnique<opt::Instruction>(
- ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(),
+ message_.is_signed());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
index 6ce5ea1..a7345a1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_struct.cpp
@@ -50,13 +50,10 @@
void TransformationAddTypeStruct::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- opt::Instruction::OperandList in_operands;
- for (auto member_type : message_.member_type_id()) {
- in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}});
- }
- ir_context->module()->AddType(MakeUnique<opt::Instruction>(
- ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ fuzzerutil::AddStructType(
+ ir_context, message_.fresh_id(),
+ std::vector<uint32_t>(message_.member_type_id().begin(),
+ message_.member_type_id().end()));
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
index f7b2fb5..10a6224 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_vector.cpp
@@ -46,13 +46,9 @@
void TransformationAddTypeVector::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- opt::Instruction::OperandList in_operands;
- in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}});
- in_operands.push_back(
- {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}});
- ir_context->module()->AddType(MakeUnique<opt::Instruction>(
- ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands));
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ fuzzerutil::AddVectorType(ir_context, message_.fresh_id(),
+ message_.component_type_id(),
+ message_.component_count());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
index cd4f22f..15af53e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_construct.cpp
@@ -40,7 +40,8 @@
}
bool TransformationCompositeConstruct::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
// We require the id for the composite constructor to be unused.
return false;
@@ -87,7 +88,20 @@
// Now check whether every component being used to initialize the composite is
// available at the desired program point.
- for (auto& component : message_.component()) {
+ for (auto component : message_.component()) {
+ auto* inst = ir_context->get_def_use_mgr()->GetDef(component);
+ if (!inst) {
+ return false;
+ }
+
+ // We should be able to create a synonym of |component| if it's not
+ // irrelevant.
+ if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ inst)) {
+ return false;
+ }
+
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
component)) {
return false;
@@ -144,17 +158,23 @@
for (uint32_t subvector_index = 0;
subvector_index < component_type->AsVector()->element_count();
subvector_index++) {
- transformation_context->GetFactManager()->AddFactDataSynonym(
- MakeDataDescriptor(component, {subvector_index}),
- MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+ if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+ component)) {
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(component, {subvector_index}),
+ MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+ }
index++;
}
} else {
// The other cases are simple: the component is made directly synonymous
// with the element of the composite being constructed.
- transformation_context->GetFactManager()->AddFactDataSynonym(
- MakeDataDescriptor(component, {}),
- MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+ if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+ component)) {
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(component, {}),
+ MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+ }
index++;
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
index 3dc3953..9f4d554 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.cpp
@@ -40,7 +40,8 @@
}
bool TransformationCompositeExtract::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
@@ -54,6 +55,14 @@
if (!composite_instruction) {
return false;
}
+ if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.composite_id()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ composite_instruction)) {
+ // |composite_id| will participate in DataSynonym facts. Thus, it can't be
+ // an irrelevant id.
+ return false;
+ }
if (auto block = ir_context->get_instr_block(composite_instruction)) {
if (composite_instruction == instruction_to_insert_before ||
!ir_context->GetDominatorAnalysis(block->GetParent())
@@ -105,17 +114,20 @@
// Add the fact that the id storing the extracted element is synonymous with
// the index into the structure.
- std::vector<uint32_t> indices;
- for (auto an_index : message_.index()) {
- indices.push_back(an_index);
+ if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+ message_.composite_id())) {
+ std::vector<uint32_t> indices;
+ for (auto an_index : message_.index()) {
+ indices.push_back(an_index);
+ }
+ protobufs::DataDescriptor data_descriptor_for_extracted_element =
+ MakeDataDescriptor(message_.composite_id(), std::move(indices));
+ protobufs::DataDescriptor data_descriptor_for_result_id =
+ MakeDataDescriptor(message_.fresh_id(), {});
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ data_descriptor_for_extracted_element, data_descriptor_for_result_id,
+ ir_context);
}
- protobufs::DataDescriptor data_descriptor_for_extracted_element =
- MakeDataDescriptor(message_.composite_id(), std::move(indices));
- protobufs::DataDescriptor data_descriptor_for_result_id =
- MakeDataDescriptor(message_.fresh_id(), {});
- transformation_context->GetFactManager()->AddFactDataSynonym(
- data_descriptor_for_extracted_element, data_descriptor_for_result_id,
- ir_context);
}
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
index 8f52d22..34df823 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_composite_extract.h
@@ -48,7 +48,8 @@
// Adds an OpCompositeConstruct instruction before the instruction identified
// by |message_.instruction_to_insert_before|, that extracts from
// |message_.composite_id| via indices |message_.index| into
- // |message_.fresh_id|. Generates a data synonym fact relating
+ // |message_.fresh_id|. If |composite_id| is not an irrelevant id,
+ // generates a data synonym fact relating
// |message_.fresh_id| to the extracted element.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
index 9c8a90f..6c2dfdf 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_context.cpp
@@ -18,10 +18,8 @@
namespace fuzz {
TransformationContext::TransformationContext(
- FactManager* transformation_context,
- spv_validator_options validator_options)
- : fact_manager_(transformation_context),
- validator_options_(validator_options) {}
+ FactManager* fact_manager, spv_validator_options validator_options)
+ : fact_manager_(fact_manager), validator_options_(validator_options) {}
TransformationContext::~TransformationContext() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
deleted file mode 100644
index 7b5b5c9..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/fuzz/transformation_copy_object.h"
-
-#include "source/fuzz/data_descriptor.h"
-#include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/instruction_descriptor.h"
-#include "source/opt/instruction.h"
-#include "source/util/make_unique.h"
-
-namespace spvtools {
-namespace fuzz {
-
-TransformationCopyObject::TransformationCopyObject(
- const protobufs::TransformationCopyObject& message)
- : message_(message) {}
-
-TransformationCopyObject::TransformationCopyObject(
- uint32_t object,
- const protobufs::InstructionDescriptor& instruction_to_insert_before,
- uint32_t fresh_id) {
- message_.set_object(object);
- *message_.mutable_instruction_to_insert_before() =
- instruction_to_insert_before;
- message_.set_fresh_id(fresh_id);
-}
-
-bool TransformationCopyObject::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
- if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
- // We require the id for the object copy to be unused.
- return false;
- }
- // The id of the object to be copied must exist
- auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
- if (!object_inst) {
- return false;
- }
- if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) {
- return false;
- }
-
- auto insert_before =
- FindInstruction(message_.instruction_to_insert_before(), ir_context);
- if (!insert_before) {
- // The instruction before which the copy should be inserted was not found.
- return false;
- }
-
- if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
- insert_before)) {
- return false;
- }
-
- // |message_object| must be available directly before the point where we want
- // to add the copy.
- return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
- message_.object());
-}
-
-void TransformationCopyObject::Apply(
- opt::IRContext* ir_context,
- TransformationContext* transformation_context) const {
- auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
- assert(object_inst && "The object to be copied must exist.");
- auto insert_before_inst =
- FindInstruction(message_.instruction_to_insert_before(), ir_context);
- auto destination_block = ir_context->get_instr_block(insert_before_inst);
- assert(destination_block && "The base instruction must be in a block.");
- auto insert_before = fuzzerutil::GetIteratorForInstruction(
- destination_block, insert_before_inst);
- assert(insert_before != destination_block->end() &&
- "There must be an instruction before which the copy can be inserted.");
-
- opt::Instruction::OperandList operands = {
- {SPV_OPERAND_TYPE_ID, {message_.object()}}};
- insert_before->InsertBefore(MakeUnique<opt::Instruction>(
- ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
- message_.fresh_id(), operands));
-
- fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
- ir_context->InvalidateAnalysesExceptFor(
- opt::IRContext::Analysis::kAnalysisNone);
-
- transformation_context->GetFactManager()->AddFactDataSynonym(
- MakeDataDescriptor(message_.object(), {}),
- MakeDataDescriptor(message_.fresh_id(), {}), ir_context);
-
- if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
- message_.object())) {
- transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
- message_.fresh_id());
- }
-}
-
-protobufs::Transformation TransformationCopyObject::ToMessage() const {
- protobufs::Transformation result;
- *result.mutable_copy_object() = message_;
- return result;
-}
-
-} // namespace fuzz
-} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h b/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
deleted file mode 100644
index 80d57ae..0000000
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_copy_object.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
-#define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
-
-#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
-#include "source/fuzz/transformation.h"
-#include "source/fuzz/transformation_context.h"
-#include "source/opt/ir_context.h"
-
-namespace spvtools {
-namespace fuzz {
-
-class TransformationCopyObject : public Transformation {
- public:
- explicit TransformationCopyObject(
- const protobufs::TransformationCopyObject& message);
-
- TransformationCopyObject(
- uint32_t object,
- const protobufs::InstructionDescriptor& instruction_to_insert_before,
- uint32_t fresh_id);
-
- // - |message_.fresh_id| must not be used by the module.
- // - |message_.object| must be a result id that is a legitimate operand for
- // OpCopyObject. In particular, it must be the id of an instruction that
- // has a result type
- // - |message_.object| must not be the target of any decoration.
- // TODO(afd): consider copying decorations along with objects.
- // - |message_.base_instruction_id| must be the result id of an instruction
- // 'base' in some block 'blk'.
- // - 'blk' must contain an instruction 'inst' located |message_.offset|
- // instructions after 'base' (if |message_.offset| = 0 then 'inst' =
- // 'base').
- // - It must be legal to insert an OpCopyObject instruction directly
- // before 'inst'.
- // - |message_.object| must be available directly before 'inst'.
- // - |message_.object| must not be a null pointer or undefined pointer (so as
- // to make it legal to load from copied pointers).
- bool IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const override;
-
- // - A new instruction,
- // %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
- // is added directly before the instruction at |message_.insert_after_id| +
- // |message_|.offset, where %ty is the type of |message_.object|.
- // - The fact that |message_.fresh_id| and |message_.object| are synonyms
- // is added to the fact manager in |transformation_context|.
- // - If |message_.object| is a pointer whose pointee value is known to be
- // irrelevant, the analogous fact is added to the fact manager in
- // |transformation_context| about |message_.fresh_id|.
- void Apply(opt::IRContext* ir_context,
- TransformationContext* transformation_context) const override;
-
- protobufs::Transformation ToMessage() const override;
-
- private:
- protobufs::TransformationCopyObject message_;
-};
-
-} // namespace fuzz
-} // namespace spvtools
-
-#endif // SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
index 5c31417..e27cd29 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
@@ -37,11 +37,13 @@
}
bool TransformationEquationInstruction::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
// The result id must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
+
// The instruction to insert before must exist.
auto insert_before =
FindInstruction(message_.instruction_to_insert_before(), ir_context);
@@ -58,13 +60,16 @@
if (inst->opcode() == SpvOpUndef) {
return false;
}
+ if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
+ return false;
+ }
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
id)) {
return false;
}
}
- return MaybeGetResultType(ir_context) != 0;
+ return MaybeGetResultTypeId(ir_context) != 0;
}
void TransformationEquationInstruction::Apply(
@@ -82,7 +87,8 @@
FindInstruction(message_.instruction_to_insert_before(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, static_cast<SpvOp>(message_.opcode()),
- MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
+ MaybeGetResultTypeId(ir_context), message_.fresh_id(),
+ std::move(in_operands)));
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
@@ -97,12 +103,108 @@
return result;
}
-uint32_t TransformationEquationInstruction::MaybeGetResultType(
+uint32_t TransformationEquationInstruction::MaybeGetResultTypeId(
opt::IRContext* ir_context) const {
- switch (static_cast<SpvOp>(message_.opcode())) {
+ auto opcode = static_cast<SpvOp>(message_.opcode());
+ switch (opcode) {
+ case SpvOpConvertUToF:
+ case SpvOpConvertSToF: {
+ if (message_.in_operand_id_size() != 1) {
+ return 0;
+ }
+
+ const auto* type = ir_context->get_type_mgr()->GetType(
+ fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0)));
+ if (!type) {
+ return 0;
+ }
+
+ if (const auto* vector = type->AsVector()) {
+ if (!vector->element_type()->AsInteger()) {
+ return 0;
+ }
+
+ if (auto element_type_id = fuzzerutil::MaybeGetFloatType(
+ ir_context, vector->element_type()->AsInteger()->width())) {
+ return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id,
+ vector->element_count());
+ }
+
+ return 0;
+ } else {
+ if (!type->AsInteger()) {
+ return 0;
+ }
+
+ return fuzzerutil::MaybeGetFloatType(ir_context,
+ type->AsInteger()->width());
+ }
+ }
+ case SpvOpBitcast: {
+ if (message_.in_operand_id_size() != 1) {
+ return 0;
+ }
+
+ const auto* operand_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+ if (!operand_inst) {
+ return 0;
+ }
+
+ const auto* operand_type =
+ ir_context->get_type_mgr()->GetType(operand_inst->type_id());
+ if (!operand_type) {
+ return 0;
+ }
+
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+ // The only constraint on the types of OpBitcast's parameters is that
+ // they must have the same number of bits. Consider improving the code
+ // below to support this in full.
+ if (const auto* vector = operand_type->AsVector()) {
+ uint32_t component_type_id;
+ if (const auto* int_type = vector->element_type()->AsInteger()) {
+ component_type_id =
+ fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+ } else if (const auto* float_type = vector->element_type()->AsFloat()) {
+ component_type_id = fuzzerutil::MaybeGetIntegerType(
+ ir_context, float_type->width(), true);
+ if (component_type_id == 0 ||
+ fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+ vector->element_count()) == 0) {
+ component_type_id = fuzzerutil::MaybeGetIntegerType(
+ ir_context, float_type->width(), false);
+ }
+ } else {
+ assert(false && "Only vectors of numerical components are supported");
+ return 0;
+ }
+
+ if (component_type_id == 0) {
+ return 0;
+ }
+
+ return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+ vector->element_count());
+ } else if (const auto* int_type = operand_type->AsInteger()) {
+ return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+ } else if (const auto* float_type = operand_type->AsFloat()) {
+ if (auto existing_id = fuzzerutil::MaybeGetIntegerType(
+ ir_context, float_type->width(), true)) {
+ return existing_id;
+ }
+
+ return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(),
+ false);
+ } else {
+ assert(false &&
+ "Operand is not a scalar or a vector of numerical type");
+ return 0;
+ }
+ }
case SpvOpIAdd:
case SpvOpISub: {
- if (message_.in_operand_id().size() != 2) {
+ if (message_.in_operand_id_size() != 2) {
return 0;
}
uint32_t first_operand_width = 0;
@@ -175,7 +277,6 @@
}
return operand_inst->type_id();
}
-
default:
assert(false && "Inappropriate opcode for equation instruction.");
return 0;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
index 7eec9c6..9ed01a8 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
@@ -63,11 +63,9 @@
protobufs::Transformation ToMessage() const override;
private:
- // A helper that, in one fell swoop, checks that |message_.opcode| and the ids
- // in |message_.in_operand_id| are compatible, and that the module contains
- // an appropriate result type id. If all is well, the result type id is
- // returned. Otherwise, 0 is returned.
- uint32_t MaybeGetResultType(opt::IRContext* ir_context) const;
+ // Returns type id for the equation instruction. Returns 0 if result type does
+ // not exist.
+ uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const;
protobufs::TransformationEquationInstruction message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp
new file mode 100644
index 0000000..a4e6d8b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.cpp
@@ -0,0 +1,178 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_invert_comparison_operator.h"
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
+ protobufs::TransformationInvertComparisonOperator message)
+ : message_(std::move(message)) {}
+
+TransformationInvertComparisonOperator::TransformationInvertComparisonOperator(
+ uint32_t operator_id, uint32_t fresh_id) {
+ message_.set_operator_id(operator_id);
+ message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationInvertComparisonOperator::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // |message_.operator_id| must be valid and inversion must be supported for
+ // it.
+ auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
+ if (!inst || !IsInversionSupported(inst->opcode())) {
+ return false;
+ }
+
+ // Check that we can insert negation instruction.
+ auto* block = ir_context->get_instr_block(inst);
+ assert(block && "Instruction must have a basic block");
+
+ auto iter = fuzzerutil::GetIteratorForInstruction(block, inst);
+ ++iter;
+ assert(iter != block->end() && "Instruction can't be the last in the block");
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+ "Can't insert negation after comparison operator");
+
+ // |message_.fresh_id| must be fresh.
+ return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
+}
+
+void TransformationInvertComparisonOperator::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id());
+ assert(inst && "Result id of an operator is invalid");
+
+ // Insert negation after |inst|.
+ auto iter = fuzzerutil::GetIteratorForInstruction(
+ ir_context->get_instr_block(inst), inst);
+ ++iter;
+
+ iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
+
+ // Change the result id of the original operator to |fresh_id|.
+ inst->SetResultId(message_.fresh_id());
+
+ // Invert the operator.
+ inst->SetOpcode(InvertOpcode(inst->opcode()));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+bool TransformationInvertComparisonOperator::IsInversionSupported(
+ SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpSGreaterThan:
+ case SpvOpSGreaterThanEqual:
+ case SpvOpSLessThan:
+ case SpvOpSLessThanEqual:
+ case SpvOpUGreaterThan:
+ case SpvOpUGreaterThanEqual:
+ case SpvOpULessThan:
+ case SpvOpULessThanEqual:
+ case SpvOpIEqual:
+ case SpvOpINotEqual:
+ case SpvOpFOrdEqual:
+ case SpvOpFUnordEqual:
+ case SpvOpFOrdNotEqual:
+ case SpvOpFUnordNotEqual:
+ case SpvOpFOrdLessThan:
+ case SpvOpFUnordLessThan:
+ case SpvOpFOrdLessThanEqual:
+ case SpvOpFUnordLessThanEqual:
+ case SpvOpFOrdGreaterThan:
+ case SpvOpFUnordGreaterThan:
+ case SpvOpFOrdGreaterThanEqual:
+ case SpvOpFUnordGreaterThanEqual:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
+ assert(IsInversionSupported(opcode) && "Inversion must be supported");
+
+ switch (opcode) {
+ case SpvOpSGreaterThan:
+ return SpvOpSLessThanEqual;
+ case SpvOpSGreaterThanEqual:
+ return SpvOpSLessThan;
+ case SpvOpSLessThan:
+ return SpvOpSGreaterThanEqual;
+ case SpvOpSLessThanEqual:
+ return SpvOpSGreaterThan;
+ case SpvOpUGreaterThan:
+ return SpvOpULessThanEqual;
+ case SpvOpUGreaterThanEqual:
+ return SpvOpULessThan;
+ case SpvOpULessThan:
+ return SpvOpUGreaterThanEqual;
+ case SpvOpULessThanEqual:
+ return SpvOpUGreaterThan;
+ case SpvOpIEqual:
+ return SpvOpINotEqual;
+ case SpvOpINotEqual:
+ return SpvOpIEqual;
+ case SpvOpFOrdEqual:
+ return SpvOpFUnordNotEqual;
+ case SpvOpFUnordEqual:
+ return SpvOpFOrdNotEqual;
+ case SpvOpFOrdNotEqual:
+ return SpvOpFUnordEqual;
+ case SpvOpFUnordNotEqual:
+ return SpvOpFOrdEqual;
+ case SpvOpFOrdLessThan:
+ return SpvOpFUnordGreaterThanEqual;
+ case SpvOpFUnordLessThan:
+ return SpvOpFOrdGreaterThanEqual;
+ case SpvOpFOrdLessThanEqual:
+ return SpvOpFUnordGreaterThan;
+ case SpvOpFUnordLessThanEqual:
+ return SpvOpFOrdGreaterThan;
+ case SpvOpFOrdGreaterThan:
+ return SpvOpFUnordLessThanEqual;
+ case SpvOpFUnordGreaterThan:
+ return SpvOpFOrdLessThanEqual;
+ case SpvOpFOrdGreaterThanEqual:
+ return SpvOpFUnordLessThan;
+ case SpvOpFUnordGreaterThanEqual:
+ return SpvOpFOrdLessThan;
+ default:
+ // The program will fail in the debug mode because of the assertion
+ // at the beginning of the function.
+ return SpvOpNop;
+ }
+}
+
+protobufs::Transformation TransformationInvertComparisonOperator::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_invert_comparison_operator() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h
new file mode 100644
index 0000000..9047a14
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_invert_comparison_operator.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_
+#define SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationInvertComparisonOperator : public Transformation {
+ public:
+ explicit TransformationInvertComparisonOperator(
+ protobufs::TransformationInvertComparisonOperator message);
+
+ TransformationInvertComparisonOperator(uint32_t operator_id,
+ uint32_t fresh_id);
+
+ // - |operator_id| should be a result id of some instruction for which
+ // IsInversionSupported returns true.
+ // - |fresh_id| must be a fresh id.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Inverts the opcode of the instruction with result id |operator_id| (e.g >=
+ // becomes <) and inserts OpLogicalNot instruction after |operator_id|. Also,
+ // changes the result id of OpLogicalNot to |operator_id| and the result id of
+ // the inverted operator to |fresh_id|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if |opcode| is supported by this transformation.
+ static bool IsInversionSupported(SpvOp opcode);
+
+ private:
+ // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.)
+ static SpvOp InvertOpcode(SpvOp opcode);
+
+ protobufs::TransformationInvertComparisonOperator message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
index 0f1220e..c4de743 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <unordered_set>
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
#include <vector>
#include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/transformation_permute_function_parameters.h"
namespace spvtools {
namespace fuzz {
@@ -29,10 +29,10 @@
TransformationPermuteFunctionParameters::
TransformationPermuteFunctionParameters(
- uint32_t function_id, uint32_t new_type_id,
+ uint32_t function_id, uint32_t function_type_fresh_id,
const std::vector<uint32_t>& permutation) {
message_.set_function_id(function_id);
- message_.set_new_type_id(new_type_id);
+ message_.set_function_type_fresh_id(function_type_fresh_id);
for (auto index : permutation) {
message_.add_permutation(index);
@@ -53,7 +53,8 @@
const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
assert(function_type && "Function type is null");
- const auto& permutation = message_.permutation();
+ std::vector<uint32_t> permutation(message_.permutation().begin(),
+ message_.permutation().end());
// Don't take return type into account
auto arg_size = function_type->NumInOperands() - 1;
@@ -63,65 +64,29 @@
return false;
}
- // Check that all indices are valid
- // and unique integers from the [0, n-1] set
- std::unordered_set<uint32_t> unique_indices;
- for (auto index : permutation) {
- // We don't compare |index| with 0 since it's an unsigned integer
- if (index >= arg_size) {
- return false;
- }
+ // Check that permutation doesn't have duplicated values.
+ assert(!fuzzerutil::HasDuplicates(permutation) &&
+ "Permutation has duplicates");
- unique_indices.insert(index);
- }
-
- // Check that permutation doesn't have duplicated values
- assert(unique_indices.size() == arg_size && "Permutation has duplicates");
-
- // Check that new function's type is valid:
- // - Has the same number of operands
- // - Has the same result type as the old one
- // - Order of arguments is permuted
- auto new_type_id = message_.new_type_id();
- const auto* new_type = ir_context->get_def_use_mgr()->GetDef(new_type_id);
-
- if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
- new_type->NumInOperands() != function_type->NumInOperands()) {
+ // Check that elements in permutation are in range [0, arg_size - 1].
+ //
+ // We must check whether the permutation is empty first because in that case
+ // |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
+ // it's an unsigned integer.
+ if (!permutation.empty() &&
+ !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
return false;
}
- // Check that both instructions have the same result type
- if (new_type->GetSingleWordInOperand(0) !=
- function_type->GetSingleWordInOperand(0)) {
- return false;
- }
-
- // Check that new function type has its arguments permuted
- for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
- // +1 to take return type into account
- if (new_type->GetSingleWordInOperand(i + 1) !=
- function_type->GetSingleWordInOperand(permutation[i] + 1)) {
- return false;
- }
- }
-
- return true;
+ return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id());
}
void TransformationPermuteFunctionParameters::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
- // Retrieve all data from the message
- uint32_t function_id = message_.function_id();
- uint32_t new_type_id = message_.new_type_id();
- const auto& permutation = message_.permutation();
-
// Find the function that will be transformed
- auto* function = fuzzerutil::FindFunction(ir_context, function_id);
+ auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
assert(function && "Can't find the function");
- // Change function's type
- function->DefInst().SetInOperand(1, {new_type_id});
-
// Adjust OpFunctionParameter instructions
// Collect ids and types from OpFunctionParameter instructions
@@ -134,7 +99,7 @@
// Permute parameters' ids and types
std::vector<uint32_t> permuted_param_id, permuted_param_type;
- for (auto index : permutation) {
+ for (auto index : message_.permutation()) {
permuted_param_id.push_back(param_id[index]);
permuted_param_type.push_back(param_type[index]);
}
@@ -149,25 +114,40 @@
});
// Fix all OpFunctionCall instructions
- ir_context->get_def_use_mgr()->ForEachUser(
- &function->DefInst(),
- [function_id, &permutation](opt::Instruction* call) {
- if (call->opcode() != SpvOpFunctionCall ||
- call->GetSingleWordInOperand(0) != function_id) {
- return;
- }
+ for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+ opt::Instruction::OperandList call_operands = {
+ call->GetInOperand(0) // Function id
+ };
- opt::Instruction::OperandList call_operands = {
- call->GetInOperand(0) // Function id
- };
+ for (auto index : message_.permutation()) {
+ // Take function id into account
+ call_operands.push_back(call->GetInOperand(index + 1));
+ }
- for (auto index : permutation) {
- // Take function id into account
- call_operands.push_back(call->GetInOperand(index + 1));
- }
+ call->SetInOperands(std::move(call_operands));
+ }
- call->SetInOperands(std::move(call_operands));
- });
+ // Update function type.
+ {
+ // We use a separate scope here since |old_function_type_inst| might become
+ // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+ auto* old_function_type_inst =
+ fuzzerutil::GetFunctionType(ir_context, function);
+ assert(old_function_type_inst && "Function must have a valid type");
+
+ std::vector<uint32_t> parameter_type_ids;
+ for (auto index : message_.permutation()) {
+ // +1 since the first operand to OpTypeFunction is a return type.
+ parameter_type_ids.push_back(
+ old_function_type_inst->GetSingleWordInOperand(index + 1));
+ }
+
+ // Change function's type.
+ fuzzerutil::UpdateFunctionType(
+ ir_context, function->result_id(), message_.function_type_fresh_id(),
+ old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids);
+ }
// Make sure our changes are analyzed
ir_context->InvalidateAnalysesExceptFor(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
index 994e4c2..8308051 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
@@ -29,11 +29,11 @@
const protobufs::TransformationPermuteFunctionParameters& message);
TransformationPermuteFunctionParameters(
- uint32_t function_id, uint32_t new_type_id,
+ uint32_t function_id, uint32_t function_type_fresh_id,
const std::vector<uint32_t>& permutation);
// - |function_id| is a valid non-entry-point OpFunction instruction
- // - |new_type_id| is a result id of a valid OpTypeFunction instruction.
+ // - |function_type_fresh_id| is a fresh id.
// New type is valid if:
// - it has the same number of operands as the old one
// - function's result type is the same as the old one
@@ -46,7 +46,7 @@
// - OpFunction instruction with |result_id == function_id| is changed.
// Its arguments are permuted according to the |permutation| vector
- // - Changed function gets a new type specified by |type_id|
+ // - Adjusts function's type to accommodate for permuted parameters.
// - Calls to the function are adjusted accordingly
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp
new file mode 100644
index 0000000..95e7a1f
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+ const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
+ : message_(message) {}
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+ uint32_t result_id, const std::vector<uint32_t>& permutation) {
+ message_.set_result_id(result_id);
+
+ for (auto index : permutation) {
+ message_.add_permutation(index);
+ }
+}
+
+bool TransformationPermutePhiOperands::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // Check that |message_.result_id| is valid.
+ const auto* inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ if (!inst || inst->opcode() != SpvOpPhi) {
+ return false;
+ }
+
+ // Check that |message_.permutation| has expected size.
+ auto expected_permutation_size = inst->NumInOperands() / 2;
+ if (static_cast<uint32_t>(message_.permutation().size()) !=
+ expected_permutation_size) {
+ return false;
+ }
+
+ // Check that |message_.permutation| has elements in range
+ // [0, expected_permutation_size - 1].
+ std::vector<uint32_t> permutation(message_.permutation().begin(),
+ message_.permutation().end());
+ assert(!fuzzerutil::HasDuplicates(permutation) &&
+ "Permutation has duplicates");
+
+ // We must check whether the permutation is empty first because in that case
+ // |expected_permutation_size - 1| will produce
+ // |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
+ return permutation.empty() ||
+ fuzzerutil::IsPermutationOfRange(permutation, 0,
+ expected_permutation_size - 1);
+}
+
+void TransformationPermutePhiOperands::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ assert(inst);
+
+ opt::Instruction::OperandList permuted_operands;
+ permuted_operands.reserve(inst->NumInOperands());
+
+ for (auto index : message_.permutation()) {
+ permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
+ permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
+ }
+
+ inst->SetInOperands(std::move(permuted_operands));
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_permute_phi_operands() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h
new file mode 100644
index 0000000..df242e3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_phi_operands.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPermutePhiOperands : public Transformation {
+ public:
+ explicit TransformationPermutePhiOperands(
+ const protobufs::TransformationPermutePhiOperands& message);
+
+ TransformationPermutePhiOperands(uint32_t result_id,
+ const std::vector<uint32_t>& permutation);
+
+ // - |result_id| must be a valid id of some OpPhi instruction in the module.
+ // - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
+ // is a number of operands to the instruction with |result_id|. All elements
+ // must be unique (i.e. |permutation.size() == n / 2|).
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Permutes operands of the OpPhi instruction with |result_id| according to
+ // the elements in |permutation|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationPermutePhiOperands message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp
new file mode 100644
index 0000000..647bfa0
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.cpp
@@ -0,0 +1,174 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_push_id_through_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
+ const spvtools::fuzz::protobufs::TransformationPushIdThroughVariable&
+ message)
+ : message_(message) {}
+
+TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
+ uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id,
+ uint32_t variable_storage_class, uint32_t initializer_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ message_.set_value_id(value_id);
+ message_.set_value_synonym_id(value_synonym_id);
+ message_.set_variable_id(variable_id);
+ message_.set_variable_storage_class(variable_storage_class);
+ message_.set_initializer_id(initializer_id);
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationPushIdThroughVariable::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ // |message_.value_synonym_id| and |message_.variable_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) ||
+ !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) {
+ return false;
+ }
+
+ // The instruction to insert before must be defined.
+ auto instruction_to_insert_before =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ if (!instruction_to_insert_before) {
+ return false;
+ }
+
+ // It must be valid to insert the OpStore and OpLoad instruction before it.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpStore, instruction_to_insert_before) ||
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpLoad, instruction_to_insert_before)) {
+ return false;
+ }
+
+ // The instruction to insert before must belong to a reachable block.
+ auto basic_block = ir_context->get_instr_block(instruction_to_insert_before);
+ if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) {
+ return false;
+ }
+
+ // The value instruction must be defined and have a type.
+ auto value_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.value_id());
+ if (!value_instruction || !value_instruction->type_id()) {
+ return false;
+ }
+
+ // We should be able to create a synonym of |value_id| if it's not irrelevant.
+ if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.value_id()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ value_instruction)) {
+ return false;
+ }
+
+ // A pointer type instruction pointing to the value type must be defined.
+ auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context, value_instruction->type_id(),
+ static_cast<SpvStorageClass>(message_.variable_storage_class()));
+ if (!pointer_type_id) {
+ return false;
+ }
+
+ // |message_.variable_storage_class| must be private or function.
+ assert((message_.variable_storage_class() == SpvStorageClassPrivate ||
+ message_.variable_storage_class() == SpvStorageClassFunction) &&
+ "The variable storage class must be private or function.");
+
+ // Check that initializer is valid.
+ const auto* constant_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+ if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+ value_instruction->type_id() != constant_inst->type_id()) {
+ return false;
+ }
+
+ // |message_.value_id| must be available at the insertion point.
+ return fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, instruction_to_insert_before, message_.value_id());
+}
+
+void TransformationPushIdThroughVariable::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ auto value_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.value_id());
+
+ // A pointer type instruction pointing to the value type must be defined.
+ auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context, value_instruction->type_id(),
+ static_cast<SpvStorageClass>(message_.variable_storage_class()));
+ assert(pointer_type_id && "The required pointer type must be available.");
+
+ // Adds whether a global or local variable.
+ if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+ fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
+ pointer_type_id, SpvStorageClassPrivate,
+ message_.initializer_id());
+ } else {
+ auto function_id = ir_context
+ ->get_instr_block(FindInstruction(
+ message_.instruction_descriptor(), ir_context))
+ ->GetParent()
+ ->result_id();
+ fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
+ pointer_type_id, function_id,
+ message_.initializer_id());
+ }
+
+ // First, insert the OpLoad instruction before |instruction_descriptor| and
+ // then insert the OpStore instruction before the OpLoad instruction.
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
+ FindInstruction(message_.instruction_descriptor(), ir_context)
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, value_instruction->type_id(),
+ message_.value_synonym_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+ if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+ message_.value_id())) {
+ // Adds the fact that |message_.value_synonym_id|
+ // and |message_.value_id| are synonymous.
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(message_.value_synonym_id(), {}),
+ MakeDataDescriptor(message_.value_id(), {}), ir_context);
+ }
+}
+
+protobufs::Transformation TransformationPushIdThroughVariable::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_push_id_through_variable() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h
new file mode 100644
index 0000000..f49db31
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_push_id_through_variable.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPushIdThroughVariable : public Transformation {
+ public:
+ explicit TransformationPushIdThroughVariable(
+ const protobufs::TransformationPushIdThroughVariable& message);
+
+ TransformationPushIdThroughVariable(
+ uint32_t value_id, uint32_t value_synonym_fresh_id,
+ uint32_t variable_fresh_id, uint32_t variable_storage_class,
+ uint32_t initializer_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |message_.value_id| must be an instruction result id that has the same
+ // type as the pointee type of |message_.pointer_id|
+ // - |message_.value_synonym_id| must be fresh
+ // - |message_.variable_id| must be fresh
+ // - |message_.variable_storage_class| must be either StorageClassPrivate or
+ // StorageClassFunction
+ // - |message_.initializer_id| must be a result id of some constant in the
+ // module. Its type must be equal to the pointee type of the variable that
+ // will be created.
+ // - |message_.instruction_descriptor| must identify an instruction
+ // which it is valid to insert the OpStore and OpLoad instructions before it
+ // and must be belongs to a reachable block.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Stores |value_id| to |variable_id|, loads |variable_id| to
+ // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id|
+ // are synonymous if |value_id| is not irrelevant.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationPushIdThroughVariable message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp
new file mode 100644
index 0000000..a93e1d4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.cpp
@@ -0,0 +1,126 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "transformation_record_synonymous_constants.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationRecordSynonymousConstants::
+ TransformationRecordSynonymousConstants(
+ const protobufs::TransformationRecordSynonymousConstants& message)
+ : message_(message) {}
+
+TransformationRecordSynonymousConstants::
+ TransformationRecordSynonymousConstants(uint32_t constant1_id,
+ uint32_t constant2_id) {
+ message_.set_constant1_id(constant1_id);
+ message_.set_constant2_id(constant2_id);
+}
+
+bool TransformationRecordSynonymousConstants::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ // The ids must be different
+ if (message_.constant1_id() == message_.constant2_id()) {
+ return false;
+ }
+
+ if (transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.constant1_id()) ||
+ transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.constant2_id())) {
+ return false;
+ }
+
+ return AreEquivalentConstants(ir_context, message_.constant1_id(),
+ message_.constant2_id());
+}
+
+void TransformationRecordSynonymousConstants::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ // Add the fact to the fact manager
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(message_.constant1_id(), {}),
+ MakeDataDescriptor(message_.constant2_id(), {}), ir_context);
+}
+
+protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_record_synonymous_constants() = message_;
+ return result;
+}
+
+bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
+ opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) {
+ const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
+ const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
+
+ // Check that the definitions exist
+ if (!def_1 || !def_2) {
+ // We don't use an assertion since otherwise the shrinker fails.
+ return false;
+ }
+
+ auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
+ auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
+
+ assert(constant1 && constant2 && "The ids must refer to constants.");
+
+ // The types must be compatible.
+ if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(),
+ def_2->type_id())) {
+ return false;
+ }
+
+ // If either constant is null, the other is equivalent iff it is zero-like
+ if (constant1->AsNullConstant()) {
+ return constant2->IsZero();
+ }
+
+ if (constant2->AsNullConstant()) {
+ return constant1->IsZero();
+ }
+
+ // If the constants are scalar, they are equal iff their words are the same
+ if (auto scalar1 = constant1->AsScalarConstant()) {
+ return scalar1->words() == constant2->AsScalarConstant()->words();
+ }
+
+ // The only remaining possibility is that the constants are composite
+ assert(constant1->AsCompositeConstant() &&
+ "Equivalence of constants can only be checked with scalar, composite "
+ "or null constants.");
+
+ // Since the types match, we already know that the number of components is
+ // the same. We check that the input operands of the definitions are all
+ // constants and that they are pairwise equivalent.
+ for (uint32_t i = 0; i < def_1->NumInOperands(); i++) {
+ if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i),
+ def_2->GetSingleWordInOperand(i))) {
+ return false;
+ }
+ }
+
+ // If we get here, all the components are equivalent
+ return true;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h
new file mode 100644
index 0000000..8cff0cd
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_record_synonymous_constants.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H
+#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H
+
+#include "source/fuzz/transformation.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationRecordSynonymousConstants : public Transformation {
+ public:
+ explicit TransformationRecordSynonymousConstants(
+ const protobufs::TransformationRecordSynonymousConstants& message);
+
+ TransformationRecordSynonymousConstants(uint32_t constant1_id,
+ uint32_t constant2_id);
+
+ // - |message_.constant_id| and |message_.synonym_id| are distinct ids
+ // of constants
+ // - |message_.constant_id| and |message_.synonym_id| refer to constants
+ // that are equivalent.
+ // Constants are equivalent if at least one of the following holds:
+ // - they are equal (i.e. they have the same type ids and equal values)
+ // - both of them represent zero-like values of compatible types
+ // - they are composite constants with compatible types and their
+ // components are pairwise equivalent
+ // Two types are compatible if at least one of the following holds:
+ // - they have the same id
+ // - they are integer scalar types with the same width
+ // - they are integer vectors and their components have the same width
+ // (this is always the case if the components are equivalent)
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Adds the fact that |message_.constant_id| and |message_.synonym_id|
+ // are synonyms to the fact manager. The module is not changed.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationRecordSynonymousConstants message_;
+
+ // Returns true if the two given constants are equivalent
+ // (the description of IsApplicable specifies the conditions they must satisfy
+ // to be considered equivalent)
+ static bool AreEquivalentConstants(opt::IRContext* ir_context,
+ uint32_t constant_id1,
+ uint32_t constant_id2);
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
index d6f17fc..6e22e7c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -243,22 +243,15 @@
return false;
}
- switch (instruction->opcode()) {
- case SpvOpPhi:
- // The instruction must not be an OpPhi, as we cannot insert a binary
- // operator instruction before an OpPhi.
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
- // scope for being less conservative.
- return false;
- case SpvOpVariable:
- // The instruction must not be an OpVariable, because (a) we cannot insert
- // a binary operator before an OpVariable, but in any case (b) the
- // constant we would be replacing is the initializer constant of the
- // OpVariable, and this cannot be the result of a binary operation.
- return false;
- default:
- return true;
+ // The instruction must not be an OpVariable, because (a) we cannot insert
+ // a binary operator before an OpVariable, but in any case (b) the
+ // constant we would be replacing is the initializer constant of the
+ // OpVariable, and this cannot be the result of a binary operation.
+ if (instruction->opcode() == SpvOpVariable) {
+ return false;
}
+
+ return true;
}
void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
@@ -281,11 +274,22 @@
opt::Instruction* result = binary_instruction.get();
auto instruction_containing_constant_use =
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
+ auto instruction_before_which_to_insert = instruction_containing_constant_use;
+
+ // If |instruction_before_which_to_insert| is an OpPhi instruction,
+ // then |binary_instruction| will be inserted into the parent block associated
+ // with the OpPhi variable operand.
+ if (instruction_containing_constant_use->opcode() == SpvOpPhi) {
+ instruction_before_which_to_insert =
+ ir_context->cfg()
+ ->block(instruction_containing_constant_use->GetSingleWordInOperand(
+ message_.id_use_descriptor().in_operand_index() + 1))
+ ->terminator();
+ }
// We want to insert the new instruction before the instruction that contains
// the use of the boolean, but we need to go backwards one more instruction if
// the using instruction is preceded by a merge instruction.
- auto instruction_before_which_to_insert = instruction_containing_constant_use;
{
opt::Instruction* previous_node =
instruction_before_which_to_insert->PreviousNode();
@@ -294,6 +298,7 @@
instruction_before_which_to_insert = previous_node;
}
}
+
instruction_before_which_to_insert->InsertBefore(
std::move(binary_instruction));
instruction_containing_constant_use->SetInOperand(
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
new file mode 100644
index 0000000..bf6996a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyMemoryWithLoadStore::
+ TransformationReplaceCopyMemoryWithLoadStore(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceCopyMemoryWithLoadStore& message)
+ : message_(message) {}
+
+TransformationReplaceCopyMemoryWithLoadStore::
+ TransformationReplaceCopyMemoryWithLoadStore(
+ uint32_t fresh_id, const protobufs::InstructionDescriptor&
+ copy_memory_instruction_descriptor) {
+ message_.set_fresh_id(fresh_id);
+ *message_.mutable_copy_memory_instruction_descriptor() =
+ copy_memory_instruction_descriptor;
+}
+
+bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // |message_.fresh_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+ return false;
+ }
+ // The instruction to be replaced must be defined and have opcode
+ // OpCopyMemory.
+ auto copy_memory_instruction = FindInstruction(
+ message_.copy_memory_instruction_descriptor(), ir_context);
+ if (!copy_memory_instruction ||
+ copy_memory_instruction->opcode() != SpvOpCopyMemory) {
+ return false;
+ }
+ return true;
+}
+
+void TransformationReplaceCopyMemoryWithLoadStore::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto copy_memory_instruction = FindInstruction(
+ message_.copy_memory_instruction_descriptor(), ir_context);
+ // |copy_memory_instruction| must be defined.
+ assert(copy_memory_instruction &&
+ copy_memory_instruction->opcode() == SpvOpCopyMemory &&
+ "The required OpCopyMemory instruction must be defined.");
+
+ // Coherence check: Both operands must be pointers.
+
+ // Get types of ids used as a source and target of |copy_memory_instruction|.
+ auto target = ir_context->get_def_use_mgr()->GetDef(
+ copy_memory_instruction->GetSingleWordInOperand(0));
+ auto source = ir_context->get_def_use_mgr()->GetDef(
+ copy_memory_instruction->GetSingleWordInOperand(1));
+ auto target_type_opcode =
+ ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode();
+ auto source_type_opcode =
+ ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode();
+
+ // Keep release-mode compilers happy. (No unused variables.)
+ (void)target;
+ (void)source;
+ (void)target_type_opcode;
+ (void)source_type_opcode;
+
+ assert(target_type_opcode == SpvOpTypePointer &&
+ source_type_opcode == SpvOpTypePointer &&
+ "Operands must be of type OpTypePointer");
+
+ // Coherence check: |source| and |target| must point to the same type.
+ uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, target->type_id());
+ uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+ ir_context, source->type_id());
+
+ // Keep release-mode compilers happy. (No unused variables.)
+ (void)target_pointee_type;
+ (void)source_pointee_type;
+
+ assert(target_pointee_type == source_pointee_type &&
+ "Operands must have the same type to which they point to.");
+
+ // First, insert the OpStore instruction before the OpCopyMemory instruction
+ // and then insert the OpLoad instruction before the OpStore instruction.
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+ FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {target->result_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
+
+ // Remove the OpCopyMemory instruction.
+ ir_context->KillInst(copy_memory_instruction);
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_copy_memory_with_load_store() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
new file mode 100644
index 0000000..70120f8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
+ public:
+ explicit TransformationReplaceCopyMemoryWithLoadStore(
+ const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
+
+ TransformationReplaceCopyMemoryWithLoadStore(
+ uint32_t fresh_id, const protobufs::InstructionDescriptor&
+ copy_memory_instruction_descriptor);
+
+ // - |message_.fresh_id| must be fresh.
+ // - |message_.copy_memory_instruction_descriptor| must refer to an
+ // OpCopyMemory instruction.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Replaces instruction OpCopyMemory with loading the source variable to an
+ // intermediate value and storing this value into the target variable of the
+ // original OpCopyMemory instruction.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationReplaceCopyMemoryWithLoadStore message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
new file mode 100644
index 0000000..05e8cda
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
@@ -0,0 +1,155 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyObjectWithStoreLoad::
+ TransformationReplaceCopyObjectWithStoreLoad(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceCopyObjectWithStoreLoad& message)
+ : message_(message) {}
+
+TransformationReplaceCopyObjectWithStoreLoad::
+ TransformationReplaceCopyObjectWithStoreLoad(
+ uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+ uint32_t variable_storage_class, uint32_t variable_initializer_id) {
+ message_.set_copy_object_result_id(copy_object_result_id);
+ message_.set_fresh_variable_id(fresh_variable_id);
+ message_.set_variable_storage_class(variable_storage_class);
+ message_.set_variable_initializer_id(variable_initializer_id);
+}
+
+bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // |message_.fresh_variable_id| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) {
+ return false;
+ }
+ auto copy_object_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+
+ // This must be a defined OpCopyObject instruction.
+ if (!copy_object_instruction ||
+ copy_object_instruction->opcode() != SpvOpCopyObject) {
+ return false;
+ }
+
+ // The opcode of the type_id instruction cannot be a OpTypePointer,
+ // because we cannot define a pointer to pointer.
+ if (ir_context->get_def_use_mgr()
+ ->GetDef(copy_object_instruction->type_id())
+ ->opcode() == SpvOpTypePointer) {
+ return false;
+ }
+
+ // It must be valid to insert the OpStore and OpLoad instruction before it.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+ copy_object_instruction) ||
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
+ copy_object_instruction)) {
+ return false;
+ }
+
+ // A pointer type instruction pointing to the value type must be defined.
+ auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context, copy_object_instruction->type_id(),
+ static_cast<SpvStorageClass>(message_.variable_storage_class()));
+ if (!pointer_type_id) {
+ return false;
+ }
+
+ // Check that initializer is valid.
+ const auto* constant_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id());
+ if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+ copy_object_instruction->type_id() != constant_inst->type_id()) {
+ return false;
+ }
+ // |message_.variable_storage_class| must be Private or Function.
+ return message_.variable_storage_class() == SpvStorageClassPrivate ||
+ message_.variable_storage_class() == SpvStorageClassFunction;
+}
+
+void TransformationReplaceCopyObjectWithStoreLoad::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ auto copy_object_instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+ // |copy_object_instruction| must be defined.
+ assert(copy_object_instruction &&
+ copy_object_instruction->opcode() == SpvOpCopyObject &&
+ "The required OpCopyObject instruction must be defined.");
+ // Get id used as a source by the OpCopyObject instruction.
+ uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2);
+ // A pointer type instruction pointing to the value type must be defined.
+ auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+ ir_context, copy_object_instruction->type_id(),
+ static_cast<SpvStorageClass>(message_.variable_storage_class()));
+ assert(pointer_type_id && "The required pointer type must be available.");
+
+ // Adds a global or local variable (according to the storage class).
+ if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+ fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
+ pointer_type_id, SpvStorageClassPrivate,
+ message_.variable_initializer_id());
+ } else {
+ auto function_id = ir_context->get_instr_block(copy_object_instruction)
+ ->GetParent()
+ ->result_id();
+ fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
+ pointer_type_id, function_id,
+ message_.variable_initializer_id());
+ }
+
+ // First, insert the OpLoad instruction before the OpCopyObject instruction
+ // and then insert the OpStore instruction before the OpLoad instruction.
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
+ copy_object_instruction
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, copy_object_instruction->type_id(),
+ message_.copy_object_result_id(),
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
+ {SPV_OPERAND_TYPE_ID, {src_operand}}})));
+ // Remove the CopyObject instruction.
+ ir_context->KillInst(copy_object_instruction);
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+ // Adds the fact that |message_.copy_object_result_id|
+ // and src_operand (id used by OpCopyObject) are synonymous.
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(message_.copy_object_result_id(), {}),
+ MakeDataDescriptor(src_operand, {}), ir_context);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_copy_object_with_store_load() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
new file mode 100644
index 0000000..db9c74e
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceCopyObjectWithStoreLoad : public Transformation {
+ public:
+ explicit TransformationReplaceCopyObjectWithStoreLoad(
+ const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message);
+
+ TransformationReplaceCopyObjectWithStoreLoad(
+ uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+ uint32_t variable_storage_class, uint32_t variable_initializer_id);
+
+ // - |message_.copy_object_result_id| must be a result id of an OpCopyObject
+ // instruction.
+ // - |message_.fresh_variable_id| must be a fresh id given to variable used by
+ // OpStore.
+ // - |message_.variable_storage_class| must be either StorageClassPrivate or
+ // StorageClassFunction.
+ // - |message_.initializer_id| must be a result id of some constant in the
+ // module. Its type must be equal to the pointee type of the variable that
+ // will be created.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Replaces instruction OpCopyObject with storing into a new variable and
+ // immediately loading from this variable to |result_id| of the original
+ // OpCopyObject instruction.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationReplaceCopyObjectWithStoreLoad message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
index e427f3c..55607e1 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -57,6 +57,24 @@
return false;
}
+ uint32_t type_id_of_interest =
+ ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
+ uint32_t type_id_synonym = ir_context->get_def_use_mgr()
+ ->GetDef(message_.synonymous_id())
+ ->type_id();
+
+ // If the id of interest and the synonym are scalar or vector integer
+ // constants with different signedness, their use can only be swapped if the
+ // instruction is agnostic to the signedness of the operand.
+ if (type_id_of_interest != type_id_synonym &&
+ fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_of_interest,
+ type_id_synonym) &&
+ !IsAgnosticToSignednessOfOperand(
+ use_instruction->opcode(),
+ message_.id_use_descriptor().in_operand_index())) {
+ return false;
+ }
+
// Is the use suitable for being replaced in principle?
if (!UseCanBeReplacedWithSynonym(
ir_context, use_instruction,
@@ -182,5 +200,46 @@
return true;
}
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
+// opcodes that are agnostic to signedness of operands to function.
+// This is not exhaustive yet.
+bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
+ SpvOp opcode, uint32_t use_in_operand_index) {
+ switch (opcode) {
+ case SpvOpSNegate:
+ case SpvOpNot:
+ case SpvOpIAdd:
+ case SpvOpISub:
+ case SpvOpIMul:
+ case SpvOpSDiv:
+ case SpvOpSRem:
+ case SpvOpSMod:
+ case SpvOpShiftRightLogical:
+ case SpvOpShiftRightArithmetic:
+ case SpvOpShiftLeftLogical:
+ case SpvOpBitwiseOr:
+ case SpvOpBitwiseXor:
+ case SpvOpBitwiseAnd:
+ case SpvOpIEqual:
+ case SpvOpINotEqual:
+ case SpvOpULessThan:
+ case SpvOpSLessThan:
+ case SpvOpUGreaterThan:
+ case SpvOpSGreaterThan:
+ case SpvOpULessThanEqual:
+ case SpvOpSLessThanEqual:
+ case SpvOpUGreaterThanEqual:
+ case SpvOpSGreaterThanEqual:
+ return true;
+ case SpvOpAccessChain:
+ // The signedness of indices does not matter.
+ return use_in_operand_index > 0;
+ default:
+ // Conservatively assume that the id cannot be swapped in other
+ // instructions.
+ return false;
+ }
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
index a5a9dfd..78878b2 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_id_with_synonym.h
@@ -64,6 +64,13 @@
opt::Instruction* use_instruction,
uint32_t use_in_operand_index);
+ // Returns true if the instruction with opcode |opcode| does not change its
+ // behaviour depending on the signedness of the operand at
+ // |use_in_operand_index|.
+ // Assumes that the operand must be the id of an integer scalar or vector.
+ static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+ uint32_t use_in_operand_index);
+
private:
protobufs::TransformationReplaceIdWithSynonym message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
new file mode 100644
index 0000000..76f083b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
@@ -0,0 +1,836 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceLinearAlgebraInstruction::
+ TransformationReplaceLinearAlgebraInstruction(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceLinearAlgebraInstruction& message)
+ : message_(message) {}
+
+TransformationReplaceLinearAlgebraInstruction::
+ TransformationReplaceLinearAlgebraInstruction(
+ const std::vector<uint32_t>& fresh_ids,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ for (auto fresh_id : fresh_ids) {
+ message_.add_fresh_ids(fresh_id);
+ }
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+ // Right now we only support certain operations. When this issue is addressed
+ // the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
+ // It must be a supported linear algebra instruction.
+ if (instruction->opcode() != SpvOpVectorTimesScalar &&
+ instruction->opcode() != SpvOpMatrixTimesScalar &&
+ instruction->opcode() != SpvOpVectorTimesMatrix &&
+ instruction->opcode() != SpvOpMatrixTimesVector &&
+ instruction->opcode() != SpvOpMatrixTimesMatrix &&
+ instruction->opcode() != SpvOpDot) {
+ return false;
+ }
+
+ // |message_.fresh_ids.size| must be the exact number of fresh ids needed to
+ // apply the transformation.
+ if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
+ GetRequiredFreshIdCount(ir_context, instruction)) {
+ return false;
+ }
+
+ // All ids in |message_.fresh_ids| must be fresh.
+ for (uint32_t fresh_id : message_.fresh_ids()) {
+ if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TransformationReplaceLinearAlgebraInstruction::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto linear_algebra_instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+
+ switch (linear_algebra_instruction->opcode()) {
+ case SpvOpVectorTimesScalar:
+ ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
+ break;
+ case SpvOpMatrixTimesScalar:
+ ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
+ break;
+ case SpvOpVectorTimesMatrix:
+ ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction);
+ break;
+ case SpvOpMatrixTimesVector:
+ ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
+ break;
+ case SpvOpMatrixTimesMatrix:
+ ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
+ break;
+ case SpvOpDot:
+ ReplaceOpDot(ir_context, linear_algebra_instruction);
+ break;
+ default:
+ assert(false && "Should be unreachable.");
+ break;
+ }
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceLinearAlgebraInstruction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_linear_algebra_instruction() = message_;
+ return result;
+}
+
+uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
+ opt::IRContext* ir_context, opt::Instruction* instruction) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
+ // Right now we only support certain operations.
+ switch (instruction->opcode()) {
+ case SpvOpVectorTimesScalar:
+ // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
+ // inserted.
+ return 2 *
+ ir_context->get_type_mgr()
+ ->GetType(ir_context->get_def_use_mgr()
+ ->GetDef(instruction->GetSingleWordInOperand(0))
+ ->type_id())
+ ->AsVector()
+ ->element_count();
+ case SpvOpMatrixTimesScalar: {
+ // For each matrix column, |1 + column.size| OpCompositeExtract,
+ // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
+ // inserted.
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(0));
+ auto matrix_type =
+ ir_context->get_type_mgr()->GetType(matrix_instruction->type_id());
+ return 2 * matrix_type->AsMatrix()->element_count() *
+ (1 + matrix_type->AsMatrix()
+ ->element_type()
+ ->AsVector()
+ ->element_count());
+ }
+ case SpvOpVectorTimesMatrix: {
+ // For each vector component, 1 OpCompositeExtract instruction will be
+ // inserted. For each matrix column, |1 + vector_component_count|
+ // OpCompositeExtract, |vector_component_count| OpFMul and
+ // |vector_component_count - 1| OpFAdd instructions will be inserted.
+ auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(0));
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(1));
+ uint32_t vector_component_count =
+ ir_context->get_type_mgr()
+ ->GetType(vector_instruction->type_id())
+ ->AsVector()
+ ->element_count();
+ uint32_t matrix_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ return vector_component_count * (3 * matrix_column_count + 1);
+ }
+ case SpvOpMatrixTimesVector: {
+ // For each matrix column, |1 + matrix_row_count| OpCompositeExtract
+ // will be inserted. For each matrix row, |matrix_column_count| OpFMul and
+ // |matrix_column_count - 1| OpFAdd instructions will be inserted. For
+ // each vector component, 1 OpCompositeExtract instruction will be
+ // inserted.
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(0));
+ uint32_t matrix_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ uint32_t matrix_row_count = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_type()
+ ->AsVector()
+ ->element_count();
+ return 3 * matrix_column_count * matrix_row_count +
+ 2 * matrix_column_count - matrix_row_count;
+ }
+ case SpvOpMatrixTimesMatrix: {
+ // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
+ // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
+ // |matrix_1_row_count * matrix_1_column_count| OpFMul,
+ // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions
+ // will be inserted.
+ auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(0));
+ uint32_t matrix_1_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_1_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ uint32_t matrix_1_row_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_1_instruction->type_id())
+ ->AsMatrix()
+ ->element_type()
+ ->AsVector()
+ ->element_count();
+
+ auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(1));
+ uint32_t matrix_2_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_2_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ return matrix_2_column_count *
+ (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
+ }
+ case SpvOpDot:
+ // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
+ // will be inserted. The first two OpFMul instructions will result the
+ // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
+ // OpFAdd will be inserted. The last OpFAdd instruction is got by changing
+ // the OpDot instruction.
+ return 4 * ir_context->get_type_mgr()
+ ->GetType(
+ ir_context->get_def_use_mgr()
+ ->GetDef(instruction->GetSingleWordInOperand(0))
+ ->type_id())
+ ->AsVector()
+ ->element_count() -
+ 2;
+ default:
+ assert(false && "Unsupported linear algebra instruction.");
+ return 0;
+ }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets OpVectorTimesScalar in operands.
+ auto vector = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ auto scalar = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+
+ uint32_t vector_component_count = ir_context->get_type_mgr()
+ ->GetType(vector->type_id())
+ ->AsVector()
+ ->element_count();
+ std::vector<uint32_t> float_multiplication_ids(vector_component_count);
+ uint32_t fresh_id_index = 0;
+
+ for (uint32_t i = 0; i < vector_component_count; i++) {
+ // Extracts |vector| component.
+ uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract, scalar->type_id(), vector_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ // Multiplies the |vector| component with the |scalar|.
+ uint32_t float_multiplication_id = message_.fresh_ids(fresh_id_index++);
+ float_multiplication_ids[i] = float_multiplication_id;
+ fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul, scalar->type_id(), float_multiplication_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_extract_id}},
+ {SPV_OPERAND_TYPE_ID, {scalar->result_id()}}})));
+ }
+
+ // The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct
+ // instruction.
+ linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+ linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
+ for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) {
+ linear_algebra_instruction->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}});
+ }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets OpMatrixTimesScalar in operands.
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+
+ // Gets matrix information.
+ uint32_t matrix_column_count = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ auto matrix_column_type = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_type();
+ uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count();
+
+ std::vector<uint32_t> composite_construct_ids(matrix_column_count);
+ uint32_t fresh_id_index = 0;
+
+ for (uint32_t i = 0; i < matrix_column_count; i++) {
+ // Extracts |matrix| column.
+ uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_column_type),
+ matrix_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ std::vector<uint32_t> float_multiplication_ids(matrix_column_size);
+
+ for (uint32_t j = 0; j < matrix_column_size; j++) {
+ // Extracts |column| component.
+ uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(),
+ column_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+ // Multiplies the |column| component with the |scalar|.
+ float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul, scalar_instruction->type_id(),
+ float_multiplication_ids[j],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
+ {SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}})));
+ }
+
+ // Constructs a new column multiplied by |scalar|.
+ opt::Instruction::OperandList composite_construct_in_operands;
+ for (uint32_t& float_multiplication_id : float_multiplication_ids) {
+ composite_construct_in_operands.push_back(
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_id}});
+ }
+ composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeConstruct,
+ ir_context->get_type_mgr()->GetId(matrix_column_type),
+ composite_construct_ids[i], composite_construct_in_operands));
+ }
+
+ // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
+ // instruction.
+ linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+ linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
+ for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
+ linear_algebra_instruction->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}});
+ }
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets vector information.
+ auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ uint32_t vector_component_count = ir_context->get_type_mgr()
+ ->GetType(vector_instruction->type_id())
+ ->AsVector()
+ ->element_count();
+ auto vector_component_type = ir_context->get_type_mgr()
+ ->GetType(vector_instruction->type_id())
+ ->AsVector()
+ ->element_type();
+
+ // Extracts vector components.
+ uint32_t fresh_id_index = 0;
+ std::vector<uint32_t> vector_component_ids(vector_component_count);
+ for (uint32_t i = 0; i < vector_component_count; i++) {
+ vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ vector_component_ids[i],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+ }
+
+ // Gets matrix information.
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+ uint32_t matrix_column_count = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ auto matrix_column_type = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_type();
+
+ std::vector<uint32_t> result_component_ids(matrix_column_count);
+ for (uint32_t i = 0; i < matrix_column_count; i++) {
+ // Extracts matrix column.
+ uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_column_type),
+ matrix_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ std::vector<uint32_t> float_multiplication_ids(vector_component_count);
+ for (uint32_t j = 0; j < vector_component_count; j++) {
+ // Extracts column component.
+ uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ column_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+ // Multiplies corresponding vector and column components.
+ float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ float_multiplication_ids[j],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}},
+ {SPV_OPERAND_TYPE_ID, {column_extract_id}}})));
+ }
+
+ // Adds the multiplication results.
+ std::vector<uint32_t> float_add_ids;
+ uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+ for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
+ float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
+ {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
+ }
+
+ result_component_ids[i] = float_add_ids.back();
+ }
+
+ // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct
+ // instruction.
+ linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+ linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
+ for (uint32_t i = 2; i < result_component_ids.size(); i++) {
+ linear_algebra_instruction->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
+ }
+
+ fuzzerutil::UpdateModuleIdBound(
+ ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets matrix information.
+ auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ uint32_t matrix_column_count = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ auto matrix_column_type = ir_context->get_type_mgr()
+ ->GetType(matrix_instruction->type_id())
+ ->AsMatrix()
+ ->element_type();
+ uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
+
+ // Extracts matrix columns.
+ uint32_t fresh_id_index = 0;
+ std::vector<uint32_t> matrix_column_ids(matrix_column_count);
+ for (uint32_t i = 0; i < matrix_column_count; i++) {
+ matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_column_type),
+ matrix_column_ids[i],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+ }
+
+ // Gets vector information.
+ auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+ auto vector_component_type = ir_context->get_type_mgr()
+ ->GetType(vector_instruction->type_id())
+ ->AsVector()
+ ->element_type();
+
+ // Extracts vector components.
+ std::vector<uint32_t> vector_component_ids(matrix_column_count);
+ for (uint32_t i = 0; i < matrix_column_count; i++) {
+ vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ vector_component_ids[i],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+ }
+
+ std::vector<uint32_t> result_component_ids(matrix_row_count);
+ for (uint32_t i = 0; i < matrix_row_count; i++) {
+ std::vector<uint32_t> float_multiplication_ids(matrix_column_count);
+ for (uint32_t j = 0; j < matrix_column_count; j++) {
+ // Extracts column component.
+ uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ column_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ // Multiplies corresponding vector and column components.
+ float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ float_multiplication_ids[j],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
+ {SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}})));
+ }
+
+ // Adds the multiplication results.
+ std::vector<uint32_t> float_add_ids;
+ uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+ for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
+ float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(vector_component_type),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
+ {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
+ }
+
+ result_component_ids[i] = float_add_ids.back();
+ }
+
+ // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct
+ // instruction.
+ linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+ linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
+ for (uint32_t i = 2; i < result_component_ids.size(); i++) {
+ linear_algebra_instruction->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
+ }
+
+ fuzzerutil::UpdateModuleIdBound(
+ ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets matrix 1 information.
+ auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ uint32_t matrix_1_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_1_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ auto matrix_1_column_type = ir_context->get_type_mgr()
+ ->GetType(matrix_1_instruction->type_id())
+ ->AsMatrix()
+ ->element_type();
+ auto matrix_1_column_component_type =
+ matrix_1_column_type->AsVector()->element_type();
+ uint32_t matrix_1_row_count =
+ matrix_1_column_type->AsVector()->element_count();
+
+ // Gets matrix 2 information.
+ auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+ uint32_t matrix_2_column_count =
+ ir_context->get_type_mgr()
+ ->GetType(matrix_2_instruction->type_id())
+ ->AsMatrix()
+ ->element_count();
+ auto matrix_2_column_type = ir_context->get_type_mgr()
+ ->GetType(matrix_2_instruction->type_id())
+ ->AsMatrix()
+ ->element_type();
+
+ uint32_t fresh_id_index = 0;
+ std::vector<uint32_t> result_column_ids(matrix_2_column_count);
+ for (uint32_t i = 0; i < matrix_2_column_count; i++) {
+ // Extracts matrix 2 column.
+ uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_2_column_type),
+ matrix_2_column_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ std::vector<uint32_t> column_component_ids(matrix_1_row_count);
+ for (uint32_t j = 0; j < matrix_1_row_count; j++) {
+ std::vector<uint32_t> float_multiplication_ids(matrix_1_column_count);
+ for (uint32_t k = 0; k < matrix_1_column_count; k++) {
+ // Extracts matrix 1 column.
+ uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+ matrix_1_column_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+ // Extracts matrix 1 column component.
+ uint32_t matrix_1_column_component_id =
+ message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+ matrix_1_column_component_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+ // Extracts matrix 2 column component.
+ uint32_t matrix_2_column_component_id =
+ message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+ matrix_2_column_component_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+ // Multiplies corresponding matrix 1 and matrix 2 column components.
+ float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+ float_multiplication_ids[k],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}},
+ {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}})));
+ }
+
+ // Adds the multiplication results.
+ std::vector<uint32_t> float_add_ids;
+ uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+ for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) {
+ float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}},
+ {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}})));
+ }
+
+ column_component_ids[j] = float_add_ids.back();
+ }
+
+ // Inserts the resulting matrix column.
+ opt::Instruction::OperandList in_operands;
+ for (auto& column_component_id : column_component_ids) {
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
+ }
+ result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeConstruct,
+ ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+ result_column_ids[i], opt::Instruction::OperandList(in_operands)));
+ }
+
+ // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
+ // instruction.
+ linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+ linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
+ for (uint32_t i = 2; i < result_column_ids.size(); i++) {
+ linear_algebra_instruction->AddOperand(
+ {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
+ }
+
+ fuzzerutil::UpdateModuleIdBound(
+ ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
+ opt::IRContext* ir_context,
+ opt::Instruction* linear_algebra_instruction) const {
+ // Gets OpDot in operands.
+ auto vector_1 = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(0));
+ auto vector_2 = ir_context->get_def_use_mgr()->GetDef(
+ linear_algebra_instruction->GetSingleWordInOperand(1));
+
+ uint32_t vectors_component_count = ir_context->get_type_mgr()
+ ->GetType(vector_1->type_id())
+ ->AsVector()
+ ->element_count();
+ std::vector<uint32_t> float_multiplication_ids(vectors_component_count);
+ uint32_t fresh_id_index = 0;
+
+ for (uint32_t i = 0; i < vectors_component_count; i++) {
+ // Extracts |vector_1| component.
+ uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ linear_algebra_instruction->type_id(), vector_1_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ // Extracts |vector_2| component.
+ uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract,
+ linear_algebra_instruction->type_id(), vector_2_extract_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+ // Multiplies the pair of components.
+ float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFMul, linear_algebra_instruction->type_id(),
+ float_multiplication_ids[i],
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}},
+ {SPV_OPERAND_TYPE_ID, {vector_2_extract_id}}})));
+ }
+
+ // If the vector has 2 components, then there will be 2 float multiplication
+ // instructions.
+ if (vectors_component_count == 2) {
+ linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+ linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]});
+ linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]});
+ } else {
+ // The first OpFAdd instruction has as operands the first two OpFMul
+ // instructions.
+ std::vector<uint32_t> float_add_ids;
+ uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+ float_add_ids.push_back(float_add_id);
+ fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+ {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+
+ // The remaining OpFAdd instructions has as operands an OpFMul and an OpFAdd
+ // instruction.
+ for (uint32_t i = 2; i < float_multiplication_ids.size() - 1; i++) {
+ float_add_id = message_.fresh_ids(fresh_id_index++);
+ fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id);
+ float_add_ids.push_back(float_add_id);
+ linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(),
+ float_add_id,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}},
+ {SPV_OPERAND_TYPE_ID, {float_add_ids[i - 2]}}})));
+ }
+
+ // The last OpFAdd instruction is got by changing some of the OpDot
+ // instruction attributes.
+ linear_algebra_instruction->SetOpcode(SpvOpFAdd);
+ linear_algebra_instruction->SetInOperand(
+ 0, {float_multiplication_ids[float_multiplication_ids.size() - 1]});
+ linear_algebra_instruction->SetInOperand(
+ 1, {float_add_ids[float_add_ids.size() - 1]});
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
new file mode 100644
index 0000000..530c1f2
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceLinearAlgebraInstruction : public Transformation {
+ public:
+ explicit TransformationReplaceLinearAlgebraInstruction(
+ const protobufs::TransformationReplaceLinearAlgebraInstruction& message);
+
+ TransformationReplaceLinearAlgebraInstruction(
+ const std::vector<uint32_t>& fresh_ids,
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |message_.fresh_ids| must be fresh ids needed to apply the
+ // transformation.
+ // - |message_.instruction_descriptor| must be a linear algebra instruction
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Replaces a linear algebra instruction.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns the number of ids needed to apply the transformation.
+ static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context,
+ opt::Instruction* instruction);
+
+ private:
+ protobufs::TransformationReplaceLinearAlgebraInstruction message_;
+
+ // Replaces an OpVectorTimesScalar instruction.
+ void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+
+ // Replaces an OpMatrixTimesScalar instruction.
+ void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+
+ // Replaces an OpVectorTimesMatrix instruction.
+ void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+
+ // Replaces an OpMatrixTimesVector instruction.
+ void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+
+ // Replaces an OpMatrixTimesMatrix instruction.
+ void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+
+ // Replaces an OpDot instruction.
+ void ReplaceOpDot(opt::IRContext* ir_context,
+ opt::Instruction* instruction) const;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
new file mode 100644
index 0000000..52964bf
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
@@ -0,0 +1,192 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "transformation_replace_load_store_with_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opcode.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kOpStoreOperandIndexTargetVariable = 0;
+const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1;
+const uint32_t kOpLoadOperandIndexSourceVariable = 2;
+} // namespace
+
+TransformationReplaceLoadStoreWithCopyMemory::
+ TransformationReplaceLoadStoreWithCopyMemory(
+ const spvtools::fuzz::protobufs::
+ TransformationReplaceLoadStoreWithCopyMemory& message)
+ : message_(message) {}
+
+TransformationReplaceLoadStoreWithCopyMemory::
+ TransformationReplaceLoadStoreWithCopyMemory(
+ const protobufs::InstructionDescriptor& load_instruction_descriptor,
+ const protobufs::InstructionDescriptor& store_instruction_descriptor) {
+ *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor;
+ *message_.mutable_store_instruction_descriptor() =
+ store_instruction_descriptor;
+}
+bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // This transformation is only applicable to the pair of OpLoad and OpStore
+ // instructions.
+ if (message_.load_instruction_descriptor().target_instruction_opcode() !=
+ SpvOpLoad) {
+ return false;
+ }
+ if (message_.store_instruction_descriptor().target_instruction_opcode() !=
+ SpvOpStore) {
+ return false;
+ }
+
+ // The OpLoad instruction must be defined.
+ auto load_instruction =
+ FindInstruction(message_.load_instruction_descriptor(), ir_context);
+ if (!load_instruction) {
+ return false;
+ }
+
+ // The OpStore instruction must be defined.
+ auto store_instruction =
+ FindInstruction(message_.store_instruction_descriptor(), ir_context);
+ if (!store_instruction) {
+ return false;
+ }
+
+ // Intermediate values of the OpLoad and the OpStore must match.
+ if (load_instruction->result_id() !=
+ store_instruction->GetSingleWordOperand(
+ kOpStoreOperandIndexIntermediateIdToWrite)) {
+ return false;
+ }
+
+ // Get storage class of the variable pointed by the source operand in OpLoad.
+ opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
+ load_instruction->GetSingleWordOperand(2));
+ SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
+ ir_context, source_id->type_id());
+
+ // Iterate over all instructions between |load_instruction| and
+ // |store_instruction|.
+ for (auto it = load_instruction; it != store_instruction;
+ it = it->NextNode()) {
+ //|load_instruction| and |store_instruction| are not in the same block.
+ if (it == nullptr) {
+ return false;
+ }
+
+ // We need to make sure that the value pointed to by the source of the
+ // OpLoad hasn't changed by the time we see the matching OpStore
+ // instruction.
+ if (IsMemoryWritingOpCode(it->opcode())) {
+ return false;
+ } else if (IsMemoryBarrierOpCode(it->opcode()) &&
+ !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TransformationReplaceLoadStoreWithCopyMemory::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ // OpLoad and OpStore instructions must be defined.
+ auto load_instruction =
+ FindInstruction(message_.load_instruction_descriptor(), ir_context);
+ assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
+ "The required OpLoad instruction must be defined.");
+ auto store_instruction =
+ FindInstruction(message_.store_instruction_descriptor(), ir_context);
+ assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
+ "The required OpStore instruction must be defined.");
+
+ // Intermediate values of the OpLoad and the OpStore must match.
+ assert(load_instruction->result_id() ==
+ store_instruction->GetSingleWordOperand(
+ kOpStoreOperandIndexIntermediateIdToWrite) &&
+ "OpLoad and OpStore must refer to the same value.");
+
+ // Get the ids of the source operand of the OpLoad and the target operand of
+ // the OpStore.
+ uint32_t source_variable_id =
+ load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable);
+ uint32_t target_variable_id = store_instruction->GetSingleWordOperand(
+ kOpStoreOperandIndexTargetVariable);
+
+ // Insert the OpCopyMemory instruction before the OpStore instruction.
+ store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCopyMemory, 0, 0,
+ opt::Instruction::OperandList(
+ {{SPV_OPERAND_TYPE_ID, {target_variable_id}},
+ {SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
+
+ // Remove the OpStore instruction.
+ ir_context->KillInst(store_instruction);
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+ SpvOp op_code) {
+ if (spvOpcodeIsAtomicOp(op_code)) {
+ return op_code != SpvOpAtomicLoad;
+ }
+ switch (op_code) {
+ case SpvOpStore:
+ case SpvOpCopyMemory:
+ case SpvOpCopyMemorySized:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
+ SpvOp op_code) {
+ switch (op_code) {
+ case SpvOpMemoryBarrier:
+ case SpvOpMemoryNamedBarrier:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::
+ IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
+ switch (storage_class) {
+ case SpvStorageClassUniformConstant:
+ case SpvStorageClassInput:
+ case SpvStorageClassUniform:
+ case SpvStorageClassPrivate:
+ case SpvStorageClassFunction:
+ return true;
+ default:
+ return false;
+ }
+}
+
+protobufs::Transformation
+TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_replace_load_store_with_copy_memory() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
new file mode 100644
index 0000000..0fb9a09
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
+ public:
+ explicit TransformationReplaceLoadStoreWithCopyMemory(
+ const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
+
+ TransformationReplaceLoadStoreWithCopyMemory(
+ const protobufs::InstructionDescriptor& load_instruction_descriptor,
+ const protobufs::InstructionDescriptor& store_instruction_descriptor);
+
+ // - |message_.load_instruction_descriptor| must identify an OpLoad
+ // instruction.
+ // - |message_.store_instruction_descriptor| must identify an OpStore
+ // instruction.
+ // - The OpStore must write the intermediate value loaded by the OpLoad.
+ // - The OpLoad and the OpStore must not have certain instruction in between
+ // (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(),
+ // IsStorageClassSafeAcrossMemoryBarriers()).
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Takes a pair of instruction descriptors to OpLoad and OpStore that have the
+ // same intermediate value and replaces the OpStore with an equivalent
+ // OpCopyMemory.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ // Checks if the instruction that has an |op_code| might write to
+ // the source operand of the OpLoad instruction.
+ static bool IsMemoryWritingOpCode(SpvOp op_code);
+
+ // Checks if the instruction that has an |op_code| is a memory barrier that
+ // could interfere with the source operand of the OpLoad instruction
+ static bool IsMemoryBarrierOpCode(SpvOp op_code);
+
+ // Checks if the |storage_class| of the source operand of the OpLoad
+ // instruction implies that this variable cannot change (due to other threads)
+ // across memory barriers.
+ static bool IsStorageClassSafeAcrossMemoryBarriers(
+ SpvStorageClass storage_class);
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationReplaceLoadStoreWithCopyMemory message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp
new file mode 100644
index 0000000..f680b63
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.cpp
@@ -0,0 +1,224 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceParameterWithGlobal::
+ TransformationReplaceParameterWithGlobal(
+ const protobufs::TransformationReplaceParameterWithGlobal& message)
+ : message_(message) {}
+
+TransformationReplaceParameterWithGlobal::
+ TransformationReplaceParameterWithGlobal(
+ uint32_t function_type_fresh_id, uint32_t parameter_id,
+ uint32_t global_variable_fresh_id) {
+ message_.set_function_type_fresh_id(function_type_fresh_id);
+ message_.set_parameter_id(parameter_id);
+ message_.set_global_variable_fresh_id(global_variable_fresh_id);
+}
+
+bool TransformationReplaceParameterWithGlobal::IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
+ // Check that |parameter_id| is valid.
+ const auto* param_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
+ if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) {
+ return false;
+ }
+
+ // Check that function exists and is not an entry point.
+ const auto* function = fuzzerutil::GetFunctionFromParameterId(
+ ir_context, message_.parameter_id());
+ if (!function ||
+ fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+ return false;
+ }
+
+ // We already know that the function has at least one parameter -
+ // |parameter_id|.
+
+ // Check that replaced parameter has valid type.
+ const auto* param_type =
+ ir_context->get_type_mgr()->GetType(param_inst->type_id());
+ assert(param_type && "Parameter has invalid type");
+ if (!IsParameterTypeSupported(*param_type)) {
+ return false;
+ }
+
+ // Check that initializer for the global variable exists in the module.
+ if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context,
+ param_inst->type_id(), false) == 0) {
+ return false;
+ }
+
+ // Check that pointer type for the global variable exists in the module.
+ if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
+ SpvStorageClassPrivate)) {
+ return false;
+ }
+
+ return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
+ fuzzerutil::IsFreshId(ir_context,
+ message_.global_variable_fresh_id()) &&
+ message_.function_type_fresh_id() !=
+ message_.global_variable_fresh_id();
+}
+
+void TransformationReplaceParameterWithGlobal::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ const auto* param_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
+ assert(param_inst && "Parameter must exist");
+
+ // Create global variable to store parameter's value.
+ fuzzerutil::AddGlobalVariable(
+ ir_context, message_.global_variable_fresh_id(),
+ fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
+ SpvStorageClassPrivate),
+ SpvStorageClassPrivate,
+ fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context,
+ param_inst->type_id(), false));
+
+ // Mark the global variable's pointee as irrelevant if replaced parameter is
+ // irrelevant.
+ if (transformation_context->GetFactManager()->IdIsIrrelevant(
+ message_.parameter_id())) {
+ transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+ message_.global_variable_fresh_id());
+ }
+
+ auto* function = fuzzerutil::GetFunctionFromParameterId(
+ ir_context, message_.parameter_id());
+ assert(function && "Function must exist");
+
+ // Insert an OpLoad instruction right after OpVariable instructions.
+ auto it = function->begin()->begin();
+ while (it != function->begin()->end() &&
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) {
+ ++it;
+ }
+
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) &&
+ "Can't insert OpLoad or OpCopyMemory into the first basic block of "
+ "the function");
+
+ it.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}}));
+
+ // Calculate the index of the replaced parameter (we need to know this to
+ // remove operands from the OpFunctionCall).
+ auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+ auto parameter_index = static_cast<uint32_t>(params.size());
+ for (uint32_t i = 0, n = static_cast<uint32_t>(params.size()); i < n; ++i) {
+ if (params[i]->result_id() == message_.parameter_id()) {
+ parameter_index = i;
+ break;
+ }
+ }
+
+ assert(parameter_index != params.size() &&
+ "Parameter must exist in the function");
+
+ // Update all relevant OpFunctionCall instructions.
+ for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
+ "Can't insert OpStore right before the function call");
+
+ // Insert an OpStore before the OpFunctionCall. +1 since the first
+ // operand of OpFunctionCall is an id of the function.
+ inst->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpStore, 0, 0,
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
+ {SPV_OPERAND_TYPE_ID,
+ {inst->GetSingleWordInOperand(parameter_index + 1)}}}));
+
+ // +1 since the first operand of OpFunctionCall is an id of the
+ // function.
+ inst->RemoveInOperand(parameter_index + 1);
+ }
+
+ // Remove the parameter from the function.
+ function->RemoveParameter(message_.parameter_id());
+
+ // Update function's type.
+ {
+ // We use a separate scope here since |old_function_type| might become a
+ // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+ auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+ assert(old_function_type && "Function has invalid type");
+
+ // +1 and -1 since the first operand is the return type id.
+ std::vector<uint32_t> parameter_type_ids;
+ for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+ if (i - 1 != parameter_index) {
+ parameter_type_ids.push_back(
+ old_function_type->GetSingleWordInOperand(i));
+ }
+ }
+
+ fuzzerutil::UpdateFunctionType(
+ ir_context, function->result_id(), message_.function_type_fresh_id(),
+ old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
+ }
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_replace_parameter_with_global() = message_;
+ return result;
+}
+
+bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
+ const opt::analysis::Type& type) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+ // Think about other type instructions we can add here.
+ switch (type.kind()) {
+ case opt::analysis::Type::kBool:
+ case opt::analysis::Type::kInteger:
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kArray:
+ case opt::analysis::Type::kMatrix:
+ case opt::analysis::Type::kVector:
+ return true;
+ case opt::analysis::Type::kStruct:
+ return std::all_of(type.AsStruct()->element_types().begin(),
+ type.AsStruct()->element_types().end(),
+ [](const opt::analysis::Type* element_type) {
+ return IsParameterTypeSupported(*element_type);
+ });
+ default:
+ return false;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h
new file mode 100644
index 0000000..49e7585
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_parameter_with_global.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceParameterWithGlobal : public Transformation {
+ public:
+ explicit TransformationReplaceParameterWithGlobal(
+ const protobufs::TransformationReplaceParameterWithGlobal& message);
+
+ TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id,
+ uint32_t parameter_id,
+ uint32_t global_variable_fresh_id);
+
+ // - |function_type_fresh_id| is a fresh id.
+ // - |parameter_id| is the result id of the parameter to replace.
+ // - |global_variable_fresh_id| is a fresh id.
+ // - |function_type_fresh_id| is not equal to |global_variable_fresh_id|.
+ // - the function that contains |parameter_id| may not be an entry-point
+ // function.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // - Removes parameter with result id |parameter_id| from its function
+ // - Adds a global variable to store the value for the parameter
+ // - Add an OpStore instruction before each function call to
+ // store parameter's value into the variable
+ // - Adds OpLoad at the beginning of the function to load the
+ // value from the variable into the old parameter's id
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if the type of the parameter is supported by this
+ // transformation.
+ static bool IsParameterTypeSupported(const opt::analysis::Type& type);
+
+ private:
+ protobufs::TransformationReplaceParameterWithGlobal message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp
new file mode 100644
index 0000000..a2aaa3a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.cpp
@@ -0,0 +1,306 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+ const protobufs::TransformationReplaceParamsWithStruct& message)
+ : message_(message) {}
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+ const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
+ uint32_t fresh_parameter_id,
+ const std::unordered_map<uint32_t, uint32_t>&
+ caller_id_to_fresh_composite_id) {
+ message_.set_fresh_function_type_id(fresh_function_type_id);
+ message_.set_fresh_parameter_id(fresh_parameter_id);
+
+ for (auto id : parameter_id) {
+ message_.add_parameter_id(id);
+ }
+
+ message_.mutable_caller_id_to_fresh_composite_id()->insert(
+ caller_id_to_fresh_composite_id.begin(),
+ caller_id_to_fresh_composite_id.end());
+}
+
+bool TransformationReplaceParamsWithStruct::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ std::vector<uint32_t> parameter_id(message_.parameter_id().begin(),
+ message_.parameter_id().end());
+
+ // Check that |parameter_id| is neither empty nor it has duplicates.
+ if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) {
+ return false;
+ }
+
+ // All ids must correspond to valid parameters of the same function.
+ // The function can't be an entry-point function.
+
+ // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+ if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) {
+ return false;
+ }
+
+ const auto* function =
+ fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]);
+ if (!function ||
+ fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+ return false;
+ }
+
+ // Compute all ids of the function's parameters.
+ std::unordered_set<uint32_t> all_parameter_ids;
+ for (const auto* param :
+ fuzzerutil::GetParameters(ir_context, function->result_id())) {
+ all_parameter_ids.insert(param->result_id());
+ }
+
+ // Check that all elements in |parameter_id| are valid.
+ for (auto id : parameter_id) {
+ // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+ if (!ir_context->get_def_use_mgr()->GetDef(id)) {
+ return false;
+ }
+
+ // Check that |id| is a result id of one of the |function|'s parameters.
+ if (!all_parameter_ids.count(id)) {
+ return false;
+ }
+
+ // Check that the parameter with result id |id| has supported type.
+ const auto* type = ir_context->get_type_mgr()->GetType(
+ fuzzerutil::GetTypeId(ir_context, id));
+ assert(type && "Parameter has invalid type");
+ if (!IsParameterTypeSupported(*type)) {
+ return false;
+ }
+ }
+
+ // We already know that the function has at least |parameter_id.size()|
+ // parameters.
+
+ // Check that a relevant OpTypeStruct exists in the module.
+ if (!MaybeGetRequiredStructType(ir_context)) {
+ return false;
+ }
+
+ // Check that |callee_id_to_fresh_composite_id| is valid.
+ for (const auto* inst :
+ fuzzerutil::GetCallers(ir_context, function->result_id())) {
+ // Check that the callee is present in the map. It's ok if the map contains
+ // more ids that there are callees (those ids will not be used).
+ if (!message_.caller_id_to_fresh_composite_id().contains(
+ inst->result_id())) {
+ return false;
+ }
+ }
+
+ // Check that all fresh ids are unique and fresh.
+ std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
+ message_.fresh_parameter_id()};
+
+ for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
+ fresh_ids.push_back(entry.second);
+ }
+
+ return !fuzzerutil::HasDuplicates(fresh_ids) &&
+ std::all_of(fresh_ids.begin(), fresh_ids.end(),
+ [ir_context](uint32_t id) {
+ return fuzzerutil::IsFreshId(ir_context, id);
+ });
+}
+
+void TransformationReplaceParamsWithStruct::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* function = fuzzerutil::GetFunctionFromParameterId(
+ ir_context, message_.parameter_id(0));
+ assert(function &&
+ "All parameters' ids should've been checked in the IsApplicable");
+
+ // Get a type id of the OpTypeStruct used as a type id of the new parameter.
+ auto struct_type_id = MaybeGetRequiredStructType(ir_context);
+ assert(struct_type_id &&
+ "IsApplicable should've guaranteed that this value isn't equal to 0");
+
+ // Add new parameter to the function.
+ function->AddParameter(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpFunctionParameter, struct_type_id,
+ message_.fresh_parameter_id(), opt::Instruction::OperandList()));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
+
+ // Compute indices of replaced parameters. This will be used to adjust
+ // OpFunctionCall instructions and create OpCompositeConstruct instructions at
+ // every call site.
+ std::vector<uint32_t> indices_of_replaced_params;
+ {
+ // We want to destroy |params| after the loop because it will contain
+ // dangling pointers when we remove parameters from the function.
+ auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+ for (auto id : message_.parameter_id()) {
+ auto it = std::find_if(params.begin(), params.end(),
+ [id](const opt::Instruction* param) {
+ return param->result_id() == id;
+ });
+ assert(it != params.end() && "Parameter's id is invalid");
+ indices_of_replaced_params.push_back(
+ static_cast<uint32_t>(it - params.begin()));
+ }
+ }
+
+ // Update all function calls.
+ for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+ // Create a list of operands for the OpCompositeConstruct instruction.
+ opt::Instruction::OperandList composite_components;
+ for (auto index : indices_of_replaced_params) {
+ // +1 since the first in operand to OpFunctionCall is the result id of
+ // the function.
+ composite_components.emplace_back(
+ std::move(inst->GetInOperand(index + 1)));
+ }
+
+ // Remove arguments from the function call. We do it in a separate loop
+ // and in reverse order to make sure we have removed correct operands.
+ for (auto it = indices_of_replaced_params.rbegin();
+ it != indices_of_replaced_params.rend(); ++it) {
+ // +1 since the first in operand to OpFunctionCall is the result id of
+ // the function.
+ inst->RemoveInOperand(*it + 1);
+ }
+
+ // Insert OpCompositeConstruct before the function call.
+ auto fresh_composite_id =
+ message_.caller_id_to_fresh_composite_id().at(inst->result_id());
+ inst->InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
+ std::move(composite_components)));
+
+ // Add a new operand to the OpFunctionCall instruction.
+ inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
+ fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id);
+ }
+
+ // Insert OpCompositeExtract instructions into the entry point block of the
+ // function and remove replaced parameters.
+ for (int i = 0; i < message_.parameter_id_size(); ++i) {
+ const auto* param_inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i));
+ assert(param_inst && "Parameter id is invalid");
+
+ // Skip all OpVariable instructions.
+ auto iter = function->begin()->begin();
+ while (iter != function->begin()->end() &&
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+ iter)) {
+ ++iter;
+ }
+
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+ iter) &&
+ "Can't extract parameter's value from the structure");
+
+ // Insert OpCompositeExtract instructions to unpack parameters' values from
+ // the struct type.
+ iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpCompositeExtract, param_inst->type_id(),
+ param_inst->result_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
+
+ function->RemoveParameter(param_inst->result_id());
+ }
+
+ // Update function's type.
+ {
+ // We use a separate scope here since |old_function_type| might become a
+ // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+ auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+ assert(old_function_type && "Function has invalid type");
+
+ // +1 since the first in operand to OpTypeFunction is the result type id
+ // of the function.
+ std::vector<uint32_t> parameter_type_ids;
+ for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+ if (std::find(indices_of_replaced_params.begin(),
+ indices_of_replaced_params.end(),
+ i - 1) == indices_of_replaced_params.end()) {
+ parameter_type_ids.push_back(
+ old_function_type->GetSingleWordInOperand(i));
+ }
+ }
+
+ parameter_type_ids.push_back(struct_type_id);
+
+ fuzzerutil::UpdateFunctionType(
+ ir_context, function->result_id(), message_.fresh_function_type_id(),
+ old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
+ }
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_replace_params_with_struct() = message_;
+ return result;
+}
+
+bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported(
+ const opt::analysis::Type& param_type) {
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+ // Consider adding support for more types of parameters.
+ switch (param_type.kind()) {
+ case opt::analysis::Type::kBool:
+ case opt::analysis::Type::kInteger:
+ case opt::analysis::Type::kFloat:
+ case opt::analysis::Type::kArray:
+ case opt::analysis::Type::kVector:
+ case opt::analysis::Type::kMatrix:
+ return true;
+ case opt::analysis::Type::kStruct:
+ return std::all_of(param_type.AsStruct()->element_types().begin(),
+ param_type.AsStruct()->element_types().end(),
+ [](const opt::analysis::Type* type) {
+ return IsParameterTypeSupported(*type);
+ });
+ default:
+ return false;
+ }
+}
+
+uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
+ opt::IRContext* ir_context) const {
+ std::vector<uint32_t> component_type_ids;
+ for (auto id : message_.parameter_id()) {
+ component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id));
+ }
+
+ return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h
new file mode 100644
index 0000000..0ff7340
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_replace_params_with_struct.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include <unordered_map>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceParamsWithStruct : public Transformation {
+ public:
+ explicit TransformationReplaceParamsWithStruct(
+ const protobufs::TransformationReplaceParamsWithStruct& message);
+
+ TransformationReplaceParamsWithStruct(
+ const std::vector<uint32_t>& parameter_id,
+ uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
+ const std::unordered_map<uint32_t, uint32_t>&
+ caller_id_to_fresh_composite_id);
+
+ // - Each element of |parameter_id| is a valid result id of some
+ // OpFunctionParameter instruction. All parameter ids must correspond to
+ // parameters of the same function. That function may not be an entry-point
+ // function.
+ // - Types of all parameters must be supported by this transformation (see
+ // IsParameterTypeSupported method).
+ // - |parameter_id| may not be empty or contain duplicates.
+ // - There must exist an OpTypeStruct instruction containing types of all
+ // replaced parameters. Type of the i'th component of the struct is equal
+ // to the type of the instruction with result id |parameter_id[i]|.
+ // - |caller_id_to_fresh_composite_id| should contain a key for at least every
+ // result id of an OpFunctionCall instruction that calls the function.
+ // - |fresh_function_type_id|, |fresh_parameter_id|,
+ // |caller_id_to_fresh_composite_id| are all fresh and unique ids.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // - Creates a new function parameter with result id |fresh_parameter_id|.
+ // Parameter's type is OpTypeStruct with each components type equal to the
+ // type of the replaced parameter.
+ // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted
+ // before each OpFunctionCall instruction.
+ // - OpCompositeExtract with result id equal to the result id of the replaced
+ // parameter is created in the function.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns true if parameter's type is supported by this transformation.
+ static bool IsParameterTypeSupported(const opt::analysis::Type& param_type);
+
+ private:
+ // Returns a result id of the OpTypeStruct instruction required by this
+ // transformation (see docs on the IsApplicable method to learn more).
+ uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
+
+ protobufs::TransformationReplaceParamsWithStruct message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
index 131a499..f52f170 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_set_memory_operands_mask.cpp
@@ -101,6 +101,14 @@
// Either add a new operand, if no mask operand was already present, or
// replace an existing mask operand.
if (original_mask_in_operand_index >= instruction->NumInOperands()) {
+ // Add first memory operand if it's missing.
+ if (message_.memory_operands_mask_index() == 1 &&
+ GetInOperandIndexForMask(*instruction, 0) >=
+ instruction->NumInOperands()) {
+ instruction->AddOperand(
+ {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
+ }
+
instruction->AddOperand(
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}});
@@ -154,11 +162,26 @@
break;
}
// If we are looking for the input operand index of the first mask, return it.
+ // This will also return a correct value if the operand is missing.
if (mask_index == 0) {
return first_mask_in_operand_index;
}
assert(mask_index == 1 && "Memory operands mask index must be 0 or 1.");
+ // Memory mask operands are optional. Thus, if the second operand exists,
+ // its index will be >= |first_mask_in_operand_index + 1|. We can reason as
+ // follows to separate the cases where the index of the second operand is
+ // equal to |first_mask_in_operand_index + 1|:
+ // - If the first memory operand doesn't exist, its value is equal to None.
+ // This means that it doesn't have additional operands following it and the
+ // condition in the if statement below will be satisfied.
+ // - If the first memory operand exists and has no additional memory operands
+ // following it, the condition in the if statement below will be satisfied
+ // and we will return the correct value from the function.
+ if (first_mask_in_operand_index + 1 >= instruction.NumInOperands()) {
+ return first_mask_in_operand_index + 1;
+ }
+
// We are looking for the input operand index of the second mask. This is a
// little complicated because, depending on the contents of the first mask,
// there may be some input operands separating the two masks.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..25f48c4
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
@@ -0,0 +1,104 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapConditionalBranchOperands::
+ TransformationSwapConditionalBranchOperands(
+ const spvtools::fuzz::protobufs::
+ TransformationSwapConditionalBranchOperands& message)
+ : message_(message) {}
+
+TransformationSwapConditionalBranchOperands::
+ TransformationSwapConditionalBranchOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+ message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationSwapConditionalBranchOperands::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ const auto* inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
+ inst->opcode() == SpvOpBranchConditional;
+}
+
+void TransformationSwapConditionalBranchOperands::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* branch_inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ assert(branch_inst);
+
+ auto* block = ir_context->get_instr_block(branch_inst);
+ assert(block);
+
+ // Compute the last instruction in the |block| that allows us to insert
+ // OpLogicalNot above it.
+ auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
+ // There might be a merge instruction before OpBranchConditional.
+ --iter;
+ }
+
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+ "We should now be able to insert SpvOpLogicalNot before |iter|");
+
+ // Get the instruction whose result is used as a condition for
+ // OpBranchConditional.
+ const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
+ branch_inst->GetSingleWordInOperand(0));
+ assert(condition_inst);
+
+ // We are swapping the labels in OpBranchConditional. This means that we must
+ // invert the guard as well. We are using OpLogicalNot for that purpose here.
+ iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLogicalNot, condition_inst->type_id(),
+ message_.fresh_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ // Update OpBranchConditional condition operand.
+ branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
+
+ // Swap label operands.
+ std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
+
+ // Additionally, swap branch weights if present.
+ if (branch_inst->NumInOperands() > 3) {
+ std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
+ }
+
+ // Make sure the changes are analyzed.
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationSwapConditionalBranchOperands::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_swap_conditional_branch_operands() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..dd68ff8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_conditional_branch_operands.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSwapConditionalBranchOperands : public Transformation {
+ public:
+ explicit TransformationSwapConditionalBranchOperands(
+ const protobufs::TransformationSwapConditionalBranchOperands& message);
+
+ TransformationSwapConditionalBranchOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id);
+
+ // - |message_.instruction_descriptor| must be a valid descriptor of some
+ // OpBranchConditional instruction in the module.
+ // - |message_.fresh_id| must be a fresh id.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before
+ // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|.
+ // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_*
+ // operands.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationSwapConditionalBranchOperands message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
index ee64292..b3bd593 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.cpp
@@ -39,7 +39,8 @@
}
bool TransformationVectorShuffle::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
// The fresh id must not already be in use.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
@@ -56,12 +57,26 @@
if (!vector1_instruction || !vector1_instruction->type_id()) {
return false;
}
+ // We should be able to create a synonym of |vector1| if it's not irrelevant.
+ if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.vector1()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ vector1_instruction)) {
+ return false;
+ }
// The second vector must be an instruction with a type id
auto vector2_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.vector2());
if (!vector2_instruction || !vector2_instruction->type_id()) {
return false;
}
+ // We should be able to create a synonym of |vector2| if it's not irrelevant.
+ if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+ message_.vector2()) &&
+ !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+ vector2_instruction)) {
+ return false;
+ }
auto vector1_type =
ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
// The first vector instruction's type must actually be a vector type.
@@ -161,9 +176,21 @@
// |component| refers.
if (component <
GetVectorType(ir_context, message_.vector1())->element_count()) {
+ // Irrelevant id cannot participate in DataSynonym facts.
+ if (transformation_context->GetFactManager()->IdIsIrrelevant(
+ message_.vector1())) {
+ continue;
+ }
+
descriptor_for_source_component =
MakeDataDescriptor(message_.vector1(), {component});
} else {
+ // Irrelevant id cannot participate in DataSynonym facts.
+ if (transformation_context->GetFactManager()->IdIsIrrelevant(
+ message_.vector2())) {
+ continue;
+ }
+
auto index_into_vector_2 =
component -
GetVectorType(ir_context, message_.vector1())->element_count();
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
index f73fc31..f911976 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_vector_shuffle.h
@@ -19,7 +19,6 @@
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
-
#include "source/opt/types.h"
namespace spvtools {
@@ -59,7 +58,9 @@
// from which it came (with undefined components being ignored). If the
// result vector is a contiguous sub-range of one of the input vectors, a
// fact is added to record that |message_.fresh_id| is synonymous with this
- // sub-range.
+ // sub-range. DataSynonym facts are added only for non-irrelevant vectors
+ // (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be
+ // created for |vector1| but not |vector2|).
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
diff --git a/third_party/SPIRV-Tools/source/name_mapper.cpp b/third_party/SPIRV-Tools/source/name_mapper.cpp
index 43fdfb3..5525bbc 100644
--- a/third_party/SPIRV-Tools/source/name_mapper.cpp
+++ b/third_party/SPIRV-Tools/source/name_mapper.cpp
@@ -153,6 +153,7 @@
CASE(SubgroupLocalInvocationId)
GLCASE(VertexIndex)
GLCASE(InstanceIndex)
+ GLCASE(BaseInstance)
CASE(SubgroupEqMaskKHR)
CASE(SubgroupGeMaskKHR)
CASE(SubgroupGtMaskKHR)
diff --git a/third_party/SPIRV-Tools/source/opcode.cpp b/third_party/SPIRV-Tools/source/opcode.cpp
index 80fe3b3..f93cfd3 100644
--- a/third_party/SPIRV-Tools/source/opcode.cpp
+++ b/third_party/SPIRV-Tools/source/opcode.cpp
@@ -413,6 +413,7 @@
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicIAdd:
+ case SpvOpAtomicFAddEXT:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:
@@ -445,7 +446,7 @@
bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
- opcode == SpvOpUnreachable;
+ opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation;
}
bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
@@ -650,6 +651,42 @@
}
}
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpTranspose:
+ case SpvOpVectorTimesScalar:
+ case SpvOpMatrixTimesScalar:
+ case SpvOpVectorTimesMatrix:
+ case SpvOpMatrixTimesVector:
+ case SpvOpMatrixTimesMatrix:
+ case SpvOpOuterProduct:
+ case SpvOpDot:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool spvOpcodeIsImageSample(const SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpImageSampleImplicitLod:
+ case SpvOpImageSampleExplicitLod:
+ case SpvOpImageSampleDrefImplicitLod:
+ case SpvOpImageSampleDrefExplicitLod:
+ case SpvOpImageSampleProjImplicitLod:
+ case SpvOpImageSampleProjExplicitLod:
+ case SpvOpImageSampleProjDrefImplicitLod:
+ case SpvOpImageSampleProjDrefExplicitLod:
+ case SpvOpImageSparseSampleImplicitLod:
+ case SpvOpImageSparseSampleExplicitLod:
+ case SpvOpImageSparseSampleDrefImplicitLod:
+ case SpvOpImageSparseSampleDrefExplicitLod:
+ return true;
+ default:
+ return false;
+ }
+}
+
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
switch (opcode) {
case SpvOpMemoryBarrier:
@@ -664,6 +701,7 @@
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicIAdd:
+ case SpvOpAtomicFAddEXT:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:
diff --git a/third_party/SPIRV-Tools/source/opcode.h b/third_party/SPIRV-Tools/source/opcode.h
index b4f0271..9aeba76 100644
--- a/third_party/SPIRV-Tools/source/opcode.h
+++ b/third_party/SPIRV-Tools/source/opcode.h
@@ -134,6 +134,12 @@
// where the order of the operands is irrelevant.
bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+// Returns true for opcodes that represents linear algebra instructions.
+bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
+
+// Returns true for opcodes that represents an image sample instruction.
+bool spvOpcodeIsImageSample(SpvOp opcode);
+
// Returns a vector containing the indices of the memory semantics <id>
// operands for |opcode|.
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp
index 755ad6a..0910595 100644
--- a/third_party/SPIRV-Tools/source/operand.cpp
+++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -455,7 +455,7 @@
return false;
}
switch (type) {
- // Blacklist non-input IDs.
+ // Deny non-input IDs.
case SPV_OPERAND_TYPE_TYPE_ID:
case SPV_OPERAND_TYPE_RESULT_ID:
return false;
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
index db2b67b..b755787 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -38,6 +38,8 @@
const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
const uint32_t kCopyMemoryTargetAddrInIdx = 0;
const uint32_t kCopyMemorySourceAddrInIdx = 1;
+const uint32_t kDebugDeclareOperandVariableIndex = 5;
+const uint32_t kGlobalVariableVariableIndex = 12;
// Sorting functor to present annotation instructions in an easy-to-process
// order. The functor orders by opcode first and falls back on unique id
@@ -105,13 +107,17 @@
IsVarOfStorage(varId, SpvStorageClassWorkgroup);
}
-void AggressiveDCEPass::AddStores(uint32_t ptrId) {
- get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](Instruction* user) {
+void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
+ get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) {
+ // If the user is not a part of |func|, skip it.
+ BasicBlock* blk = context()->get_instr_block(user);
+ if (blk && blk->GetParent() != func) return;
+
switch (user->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
case SpvOpCopyObject:
- this->AddStores(user->result_id());
+ this->AddStores(func, user->result_id());
break;
case SpvOpLoad:
break;
@@ -131,11 +137,11 @@
}
bool AggressiveDCEPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -169,13 +175,13 @@
return IsDead(tInst);
}
-void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
+void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
// Only process locals
if (!IsLocalVar(varId)) return;
// Return if already processed
if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
// Mark all stores to varId as live
- AddStores(varId);
+ AddStores(func, varId);
// Cache varId as processed
live_local_vars_.insert(varId);
}
@@ -332,6 +338,7 @@
call_in_func_ = false;
func_is_entry_point_ = false;
private_stores_.clear();
+ live_local_vars_.clear();
// Stacks to keep track of when we are inside an if- or loop-construct.
// When immediately inside an if- or loop-construct, we do not initially
// mark branches live. All other branches must be marked live.
@@ -454,7 +461,7 @@
uint32_t varId;
(void)GetPtr(liveInst, &varId);
if (varId != 0) {
- ProcessLoad(varId);
+ ProcessLoad(func, varId);
}
// Process memory copies like loads
} else if (liveInst->opcode() == SpvOpCopyMemory ||
@@ -463,31 +470,44 @@
(void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
&varId);
if (varId != 0) {
- ProcessLoad(varId);
+ ProcessLoad(func, varId);
}
+ // If DebugDeclare, process as load of variable
+ } else if (liveInst->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare) {
+ uint32_t varId =
+ liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ ProcessLoad(func, varId);
+ // If DebugValue with Deref, process as load of variable
+ } else if (liveInst->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugValue) {
+ uint32_t varId = context()
+ ->get_debug_info_mgr()
+ ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
+ if (varId != 0) ProcessLoad(func, varId);
// If merge, add other branches that are part of its control structure
} else if (liveInst->opcode() == SpvOpLoopMerge ||
liveInst->opcode() == SpvOpSelectionMerge) {
AddBreaksAndContinuesToWorklist(liveInst);
// If function call, treat as if it loads from all pointer arguments
} else if (liveInst->opcode() == SpvOpFunctionCall) {
- liveInst->ForEachInId([this](const uint32_t* iid) {
+ liveInst->ForEachInId([this, func](const uint32_t* iid) {
// Skip non-ptr args
if (!IsPtr(*iid)) return;
uint32_t varId;
(void)GetPtr(*iid, &varId);
- ProcessLoad(varId);
+ ProcessLoad(func, varId);
});
// If function parameter, treat as if it's result id is loaded from
} else if (liveInst->opcode() == SpvOpFunctionParameter) {
- ProcessLoad(liveInst->result_id());
+ ProcessLoad(func, liveInst->result_id());
// We treat an OpImageTexelPointer as a load of the pointer, and
// that value is manipulated to get the result.
} else if (liveInst->opcode() == SpvOpImageTexelPointer) {
uint32_t varId;
(void)GetPtr(liveInst, &varId);
if (varId != 0) {
- ProcessLoad(varId);
+ ProcessLoad(func, varId);
}
}
@@ -616,6 +636,18 @@
}
}
}
+
+ // For each DebugInfo GlobalVariable keep all operands except the Variable.
+ // Later, if the variable is dead, we will set the operand to DebugInfoNone.
+ for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+ if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable)
+ continue;
+ dbg.ForEachInId([this](const uint32_t* iid) {
+ Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+ if (inInst->opcode() == SpvOpVariable) return;
+ AddToWorklist(inInst);
+ });
+ }
}
Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -835,6 +867,26 @@
}
}
+ for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+ if (!IsDead(&dbg)) continue;
+ // Save GlobalVariable if its variable is live, otherwise null out variable
+ // index
+ if (dbg.GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugGlobalVariable) {
+ auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
+ Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
+ if (!IsDead(var_inst)) continue;
+ context()->ForgetUses(&dbg);
+ dbg.SetOperand(
+ kGlobalVariableVariableIndex,
+ {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
+ context()->AnalyzeUses(&dbg);
+ continue;
+ }
+ to_kill_.push_back(&dbg);
+ modified = true;
+ }
+
// Since ADCE is disabled for non-shaders, we don't check for export linkage
// attributes here.
for (auto& val : get_module()->types_values()) {
@@ -882,14 +934,14 @@
AggressiveDCEPass::AggressiveDCEPass() = default;
Pass::Status AggressiveDCEPass::Process() {
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
InitExtensions();
return ProcessImpl();
}
void AggressiveDCEPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
@@ -934,6 +986,7 @@
"SPV_KHR_ray_tracing",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
+ "SPV_KHR_terminate_invocation",
});
}
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
index c043a96..2ce5b57 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.h
@@ -85,9 +85,9 @@
// Add all store instruction which use |ptrId|, directly or indirectly,
// to the live instruction worklist.
- void AddStores(uint32_t ptrId);
+ void AddStores(Function* func, uint32_t ptrId);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are supported by this pass.
@@ -99,7 +99,7 @@
bool IsTargetDead(Instruction* inst);
// If |varId| is local, mark all stores of varId as live.
- void ProcessLoad(uint32_t varId);
+ void ProcessLoad(Function* func, uint32_t varId);
// If |bp| is structured header block, returns true and sets |mergeInst| to
// the merge instruction, |branchInst| to the branch and |mergeBlockId| to the
@@ -191,7 +191,7 @@
std::vector<Instruction*> to_kill_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/code_sink.cpp b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
index 4c88cd4..e49029f 100644
--- a/third_party/SPIRV-Tools/source/opt/code_sink.cpp
+++ b/third_party/SPIRV-Tools/source/opt/code_sink.cpp
@@ -214,6 +214,7 @@
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicIAdd:
+ case SpvOpAtomicFAddEXT:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:
diff --git a/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp b/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
index b3b90da..67a97b6 100644
--- a/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
+++ b/third_party/SPIRV-Tools/source/opt/copy_prop_arrays.cpp
@@ -29,6 +29,12 @@
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerPointeeInIdx = 1;
+bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
+ auto dbg_opcode = di->GetOpenCL100DebugOpcode();
+ return dbg_opcode == OpenCLDebugInfo100DebugDeclare ||
+ dbg_opcode == OpenCLDebugInfo100DebugValue;
+}
+
} // namespace
Pass::Status CopyPropagateArrays::Process() {
@@ -188,6 +194,8 @@
return ptr_inst->opcode() == SpvOpVariable &&
store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
ptr_inst->result_id();
+ } else if (IsOpenCL100DebugDeclareOrValue(use)) {
+ return true;
}
// Some other instruction. Be conservative.
return false;
@@ -492,6 +500,8 @@
const_mgr,
type](Instruction* use,
uint32_t) {
+ if (IsOpenCL100DebugDeclareOrValue(use)) return true;
+
switch (use->opcode()) {
case SpvOpLoad: {
analysis::Pointer* pointer_type = type->AsPointer();
@@ -565,6 +575,7 @@
}
});
}
+
void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
Instruction* new_ptr_inst) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -580,6 +591,52 @@
for (auto pair : uses) {
Instruction* use = pair.first;
uint32_t index = pair.second;
+
+ if (use->IsOpenCL100DebugInstr()) {
+ switch (use->GetOpenCL100DebugOpcode()) {
+ case OpenCLDebugInfo100DebugDeclare: {
+ if (new_ptr_inst->opcode() == SpvOpVariable ||
+ new_ptr_inst->opcode() == SpvOpFunctionParameter) {
+ context()->ForgetUses(use);
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+ context()->AnalyzeUses(use);
+ } else {
+ // Based on the spec, we cannot use a pointer other than OpVariable
+ // or OpFunctionParameter for DebugDeclare. We have to use
+ // DebugValue with Deref.
+
+ context()->ForgetUses(use);
+
+ // Change DebugDeclare to DebugValue.
+ use->SetOperand(
+ index - 2,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)});
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+
+ // Add Deref operation.
+ Instruction* dbg_expr =
+ def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
+ auto* deref_expr_instr =
+ context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
+ use->SetOperand(index + 1, {deref_expr_instr->result_id()});
+
+ context()->AnalyzeUses(deref_expr_instr);
+ context()->AnalyzeUses(use);
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugValue:
+ context()->ForgetUses(use);
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+ context()->AnalyzeUses(use);
+ break;
+ default:
+ assert(false && "Don't know how to rewrite instruction");
+ break;
+ }
+ continue;
+ }
+
switch (use->opcode()) {
case SpvOpLoad: {
// Replace the actual use.
diff --git a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
index 16d9fd5..0054f57 100644
--- a/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dead_branch_elim_pass.cpp
@@ -41,6 +41,7 @@
bool condIsConst;
Instruction* cInst = get_def_use_mgr()->GetDef(condId);
switch (cInst->opcode()) {
+ case SpvOpConstantNull:
case SpvOpConstantFalse: {
*condVal = false;
condIsConst = true;
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
index 9d98584..6c33764 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.cpp
@@ -24,7 +24,21 @@
static const uint32_t kLineOperandIndexDebugFunction = 7;
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugFunctionOperandParentIndex = 9;
+static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
+static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+static const uint32_t kDebugExpressOperandOperationIndex = 4;
+static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
+static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugOperationOperandOperationIndex = 4;
+static const uint32_t kOpVariableOperandStorageClassIndex = 2;
+static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
+static const uint32_t kExtInstInstructionInIdx = 1;
+static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
+static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
namespace spvtools {
namespace opt {
@@ -36,7 +50,8 @@
assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugInlinedAt);
if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
- dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
+ dbg_inlined_at->AddOperand(
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
} else {
dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
{inlined_operand});
@@ -53,6 +68,12 @@
kDebugInlinedAtOperandInlinedIndex);
}
+bool IsEmptyDebugExpression(Instruction* instr) {
+ return instr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugExpression &&
+ instr->NumOperands() == kDebugExpressOperandOperationIndex;
+}
+
} // namespace
DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
@@ -83,6 +104,20 @@
fn_id_to_dbg_fn_[fn_id] = inst;
}
+void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
+ Instruction* dbg_declare) {
+ assert(dbg_declare->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugDeclare ||
+ dbg_declare->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugValue);
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
+ if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
+ var_id_to_dbg_decl_[var_id] = {dbg_declare};
+ } else {
+ dbg_decl_itr->second.insert(dbg_declare);
+ }
+}
+
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope) {
if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
@@ -139,10 +174,12 @@
// |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
// into the Inlined operand of this new DebugInlinedAt.
if (scope.GetInlinedAt() != kNoInlinedAt) {
- inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
- {scope.GetInlinedAt()}});
+ inlined_at->AddOperand(
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
}
RegisterDbgInst(inlined_at.get());
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
return result_id;
}
@@ -210,6 +247,51 @@
return chain_head_id;
}
+Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
+ if (deref_operation_ != nullptr) return deref_operation_;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> deref_operation(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
+ {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
+ {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
+ }));
+
+ // Add to the front of |ext_inst_debuginfo_|.
+ deref_operation_ =
+ context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+ std::move(deref_operation));
+
+ RegisterDbgInst(deref_operation_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_);
+ return deref_operation_;
+}
+
+Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
+ assert(dbg_expr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugExpression);
+ std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
+ deref_expr->SetResultId(context()->TakeNextId());
+ deref_expr->InsertOperand(
+ kDebugExpressOperandOperationIndex,
+ {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
+ auto* deref_expr_instr =
+ context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
+ AnalyzeDebugInst(deref_expr_instr);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr);
+ return deref_expr_instr;
+}
+
Instruction* DebugInfoManager::GetDebugInfoNone() {
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
@@ -218,11 +300,11 @@
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
- {SPV_OPERAND_TYPE_RESULT_ID,
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
- {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
}));
@@ -232,9 +314,38 @@
std::move(dbg_info_none_inst));
RegisterDbgInst(debug_info_none_inst_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
return debug_info_none_inst_;
}
+Instruction* DebugInfoManager::GetEmptyDebugExpression() {
+ if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
+ }));
+
+ // Add to the front of |ext_inst_debuginfo_|.
+ empty_debug_expr_inst_ =
+ context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+ std::move(empty_debug_expr));
+
+ RegisterDbgInst(empty_debug_expr_inst_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
+ return empty_debug_expr_inst_;
+}
+
Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
if (inlined_at == nullptr) return nullptr;
@@ -252,15 +363,189 @@
std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
new_inlined_at->SetResultId(context()->TakeNextId());
RegisterDbgInst(new_inlined_at.get());
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
if (insert_before != nullptr)
return insert_before->InsertBefore(std::move(new_inlined_at));
return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
std::move(new_inlined_at));
}
+bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+ return dbg_decl_itr != var_id_to_dbg_decl_.end();
+}
+
+void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+ if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+ // We intentionally copy the list of DebugDeclare instructions because
+ // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we
+ // directly use |dbg_decl_itr->second|, it accesses a dangling pointer.
+ auto copy_dbg_decls = dbg_decl_itr->second;
+
+ for (auto* dbg_decl : copy_dbg_decls) {
+ context()->KillInst(dbg_decl);
+ }
+ var_id_to_dbg_decl_.erase(dbg_decl_itr);
+ }
+}
+
+uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
+ auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
+ assert(dbg_scope_itr != id_to_dbg_inst_.end());
+ OpenCLDebugInfo100Instructions debug_opcode =
+ dbg_scope_itr->second->GetOpenCL100DebugOpcode();
+ uint32_t parent_scope = kNoDebugScope;
+ switch (debug_opcode) {
+ case OpenCLDebugInfo100DebugFunction:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugFunctionOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugLexicalBlock:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugLexicalBlockOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugTypeComposite:
+ parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+ kDebugTypeCompositeOperandParentIndex);
+ break;
+ case OpenCLDebugInfo100DebugCompilationUnit:
+ // DebugCompilationUnit does not have a parent scope.
+ break;
+ default:
+ assert(false &&
+ "Unreachable. A debug scope instruction must be "
+ "DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
+ "or DebugCompilationUnit.");
+ break;
+ }
+ return parent_scope;
+}
+
+bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
+ uint32_t ancestor_scope_itr = scope;
+ while (ancestor_scope_itr != kNoDebugScope) {
+ if (ancestor == ancestor_scope_itr) return true;
+ ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
+ }
+ return false;
+}
+
+bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
+ uint32_t instr_scope_id) {
+ if (instr_scope_id == kNoDebugScope) return false;
+
+ uint32_t dbg_local_var_id =
+ dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
+ auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
+ assert(dbg_local_var_itr != id_to_dbg_inst_.end());
+ uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
+ kDebugLocalVariableOperandParentIndex);
+
+ // If the scope of DebugDeclare is an ancestor scope of the instruction's
+ // scope, the local variable is visible to the instruction.
+ return IsAncestorOfScope(instr_scope_id, decl_scope_id);
+}
+
+void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
+ uint32_t variable_id, uint32_t value_id,
+ Instruction* insert_pos) {
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+ if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+
+ uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
+ for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
+ if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> new_dbg_value(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {dbg_decl_or_val->GetSingleWordOperand(
+ kDebugValueOperandLocalVariableIndex)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {GetEmptyDebugExpression()->result_id()}},
+ }));
+
+ if (dbg_decl_or_val->NumOperands() >
+ kDebugValueOperandExpressionIndex + 1) {
+ assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugValue);
+ for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
+ i < dbg_decl_or_val->NumOperands(); ++i) {
+ new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {dbg_decl_or_val->GetSingleWordOperand(i)}});
+ }
+ }
+
+ // Avoid inserting the new DebugValue between OpPhi or OpVariable
+ // instructions.
+ Instruction* insert_before = insert_pos->NextNode();
+ while (insert_before->opcode() == SpvOpPhi ||
+ insert_before->opcode() == SpvOpVariable) {
+ insert_before = insert_before->NextNode();
+ }
+
+ Instruction* added_dbg_value =
+ insert_before->InsertBefore(std::move(new_dbg_value));
+ added_dbg_value->UpdateDebugInfo(scope_and_line);
+ AnalyzeDebugInst(added_dbg_value);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+ if (context()->AreAnalysesValid(
+ IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+ auto insert_blk = context()->get_instr_block(insert_before);
+ context()->set_instr_block(added_dbg_value, insert_blk);
+ }
+ }
+}
+
+uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
+ Instruction* inst) {
+ if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
+
+ auto* expr =
+ GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
+ if (expr == nullptr) return 0;
+ if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
+
+ auto* operation = GetDbgInst(
+ expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
+ if (operation == nullptr) return 0;
+ if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
+ OpenCLDebugInfo100Deref) {
+ return 0;
+ }
+
+ uint32_t var_id =
+ inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
+ assert(false &&
+ "Checking a DebugValue can be used for declare needs DefUseManager");
+ return 0;
+ }
+
+ auto* var = context()->get_def_use_mgr()->GetDef(var_id);
+ if (var->opcode() == SpvOpVariable &&
+ SpvStorageClass(var->GetSingleWordOperand(
+ kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
+ return var_id;
+ }
+ return 0;
+}
+
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
- if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
- return;
+ if (!dbg_inst->IsOpenCL100DebugInstr()) return;
RegisterDbgInst(dbg_inst);
@@ -271,27 +556,175 @@
RegisterDbgFunction(dbg_inst);
}
+ if (deref_operation_ == nullptr &&
+ dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
+ dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
+ OpenCLDebugInfo100Deref) {
+ deref_operation_ = dbg_inst;
+ }
+
if (debug_info_none_inst_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
}
+
+ if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
+ empty_debug_expr_inst_ = dbg_inst;
+ }
+
+ if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+ uint32_t var_id =
+ dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ RegisterDbgDeclare(var_id, dbg_inst);
+ }
+
+ if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
+ RegisterDbgDeclare(var_id, dbg_inst);
+ }
+}
+
+void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
+ Instruction* dbg_global_var, Instruction* local_var) {
+ if (dbg_global_var->GetOpenCL100DebugOpcode() !=
+ OpenCLDebugInfo100DebugGlobalVariable) {
+ return;
+ }
+ assert(local_var->opcode() == SpvOpVariable ||
+ local_var->opcode() == SpvOpFunctionParameter);
+
+ // Convert |dbg_global_var| to DebugLocalVariable
+ dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
+ {OpenCLDebugInfo100DebugLocalVariable});
+ auto flags = dbg_global_var->GetSingleWordOperand(
+ kDebugGlobalVariableOperandFlagsIndex);
+ for (uint32_t i = dbg_global_var->NumInOperands() - 1;
+ i >= kDebugLocalVariableOperandFlagsIndex; --i) {
+ dbg_global_var->RemoveOperand(i);
+ }
+ dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
+ context()->ForgetUses(dbg_global_var);
+ context()->AnalyzeUses(dbg_global_var);
+
+ // Create a DebugDeclare
+ std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ context()->TakeNextId(),
+ {
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {dbg_global_var->result_id()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {GetEmptyDebugExpression()->result_id()}},
+ }));
+ auto* added_dbg_decl =
+ local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
+ if (context()->AreAnalysesValid(
+ IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+ auto insert_blk = context()->get_instr_block(local_var);
+ context()->set_instr_block(added_dbg_decl, insert_blk);
+ }
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
+ deref_operation_ = nullptr;
debug_info_none_inst_ = nullptr;
+ empty_debug_expr_inst_ = nullptr;
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
+ // Move |empty_debug_expr_inst_| to the beginning of the debug instruction
+ // list.
+ if (empty_debug_expr_inst_ != nullptr &&
+ empty_debug_expr_inst_->PreviousNode() != nullptr &&
+ empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
+ empty_debug_expr_inst_->InsertBefore(
+ &*context()->module()->ext_inst_debuginfo_begin());
+ }
+
// Move |debug_info_none_inst_| to the beginning of the debug instruction
// list.
if (debug_info_none_inst_ != nullptr &&
debug_info_none_inst_->PreviousNode() != nullptr &&
- debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
- OpenCLDebugInfo100InstructionsMax) {
+ debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
debug_info_none_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
}
+void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
+ if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
+ return;
+ }
+
+ id_to_dbg_inst_.erase(instr->result_id());
+
+ if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+ auto fn_id =
+ instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+ fn_id_to_dbg_fn_.erase(fn_id);
+ }
+
+ if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+ instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+ auto var_or_value_id =
+ instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
+ if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+ dbg_decl_itr->second.erase(instr);
+ }
+ }
+
+ if (deref_operation_ == instr) {
+ deref_operation_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (instr != &*dbg_instr_itr &&
+ dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugOperation &&
+ dbg_instr_itr->GetSingleWordOperand(
+ kDebugOperationOperandOperationIndex) ==
+ OpenCLDebugInfo100Deref) {
+ deref_operation_ = &*dbg_instr_itr;
+ break;
+ }
+ }
+ }
+
+ if (debug_info_none_inst_ == instr) {
+ debug_info_none_inst_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (instr != &*dbg_instr_itr &&
+ dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugInfoNone) {
+ debug_info_none_inst_ = &*dbg_instr_itr;
+ break;
+ }
+ }
+ }
+
+ if (empty_debug_expr_inst_ == instr) {
+ empty_debug_expr_inst_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
+ empty_debug_expr_inst_ = &*dbg_instr_itr;
+ break;
+ }
+ }
+ }
+}
+
} // namespace analysis
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
index 0c7186e..6592109 100644
--- a/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/debug_info_manager.h
@@ -16,6 +16,7 @@
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
#include <unordered_map>
+#include <unordered_set>
#include "source/opt/instruction.h"
#include "source/opt/module.h"
@@ -94,6 +95,10 @@
uint32_t CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope);
+ // Clones DebugExpress instruction |dbg_expr| and add Deref Operation
+ // in the front of the Operation list of |dbg_expr|.
+ Instruction* DerefDebugExpression(Instruction* dbg_expr);
+
// Returns a DebugInfoNone instruction.
Instruction* GetDebugInfoNone();
@@ -128,6 +133,31 @@
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
DebugInlinedAtContext* inlined_at_ctx);
+ // Return true if |variable_id| has DebugDeclare or DebugVal.
+ bool IsDebugDeclared(uint32_t variable_id);
+
+ // Kill all DebugDeclares for |variable_id|
+ void KillDebugDeclares(uint32_t variable_id);
+
+ // Generates a DebugValue instruction with value |value_id| for every local
+ // variable that is in the scope of |scope_and_line| and whose memory is
+ // |variable_id| and inserts it after the instruction |insert_pos|.
+ void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
+ uint32_t value_id, Instruction* insert_pos);
+
+ // Erases |instr| from data structures of this class.
+ void ClearDebugInfo(Instruction* instr);
+
+ // Returns the id of Value operand if |inst| is DebugValue who has Deref
+ // operation and its Value operand is a result id of OpVariable with
+ // Function storage class. Otherwise, returns 0.
+ uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
+
+ // Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and
+ // creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|.
+ void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var,
+ Instruction* local_var);
+
private:
IRContext* context() { return context_; }
@@ -139,6 +169,9 @@
// does not exists.
Instruction* GetDbgInst(uint32_t id);
+ // Returns a DebugOperation instruction with OpCode Deref.
+ Instruction* GetDebugOperationWithDeref();
+
// Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
// |inst| as a key.
void RegisterDbgInst(Instruction* inst);
@@ -147,6 +180,26 @@
// in |inst| must not already be registered.
void RegisterDbgFunction(Instruction* inst);
+ // Register the DebugDeclare or DebugValue with Deref operation
+ // |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id
+ // |var_id| as a key.
+ void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
+
+ // Returns a DebugExpression instruction without Operation operands.
+ Instruction* GetEmptyDebugExpression();
+
+ // Returns true if a scope |ancestor| is |scope| or an ancestor scope
+ // of |scope|.
+ bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
+
+ // Returns true if the declaration of a local variable |dbg_declare|
+ // is visible in the scope of an instruction |instr_scope_id|.
+ bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
+ uint32_t instr_scope_id);
+
+ // Returns the parent scope of the scope |child_scope|.
+ uint32_t GetParentScope(uint32_t child_scope);
+
IRContext* context_;
// Mapping from ids of OpenCL.DebugInfo.100 extension instructions
@@ -157,9 +210,22 @@
// operand is the function.
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
+ // Mapping from variable or value ids to DebugDeclare or DebugValue
+ // instructions whose operand is the variable or value.
+ std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
+ var_id_to_dbg_decl_;
+
+ // DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
+ Instruction* deref_operation_;
+
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;
+
+ // DebugExpression instruction without Operation operands. We need only
+ // a single DebugExpression without Operation operands. To reuse the
+ // existing one, we keep it using this member variable.
+ Instruction* empty_debug_expr_inst_;
};
} // namespace analysis
diff --git a/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp b/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
index 1f25b33..b68549a 100644
--- a/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
+++ b/third_party/SPIRV-Tools/source/opt/desc_sroa.cpp
@@ -56,7 +56,23 @@
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
Instruction* var_type_inst =
context()->get_def_use_mgr()->GetDef(var_type_id);
- if (var_type_inst->opcode() != SpvOpTypeArray) {
+ if (var_type_inst->opcode() != SpvOpTypeArray &&
+ var_type_inst->opcode() != SpvOpTypeStruct) {
+ return false;
+ }
+
+ // All structures with descriptor assignments must be replaced by variables,
+ // one for each of their members - with the exceptions of buffers.
+ // Buffers are represented as structures, but we shouldn't replace a buffer
+ // with its elements. All buffers have offset decorations for members of their
+ // structure types.
+ bool has_offset_decoration = false;
+ context()->get_decoration_mgr()->ForEachDecoration(
+ var_type_inst->result_id(), SpvDecorationOffset,
+ [&has_offset_decoration](const Instruction&) {
+ has_offset_decoration = true;
+ });
+ if (has_offset_decoration) {
return false;
}
@@ -84,9 +100,11 @@
}
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
- std::vector<Instruction*> work_list;
+ std::vector<Instruction*> access_chain_work_list;
+ std::vector<Instruction*> load_work_list;
bool failed = !get_def_use_mgr()->WhileEachUser(
- var->result_id(), [this, &work_list](Instruction* use) {
+ var->result_id(),
+ [this, &access_chain_work_list, &load_work_list](Instruction* use) {
if (use->opcode() == SpvOpName) {
return true;
}
@@ -98,7 +116,10 @@
switch (use->opcode()) {
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
- work_list.push_back(use);
+ access_chain_work_list.push_back(use);
+ return true;
+ case SpvOpLoad:
+ load_work_list.push_back(use);
return true;
default:
context()->EmitErrorMessage(
@@ -112,11 +133,16 @@
return false;
}
- for (Instruction* use : work_list) {
+ for (Instruction* use : access_chain_work_list) {
if (!ReplaceAccessChain(var, use)) {
return false;
}
}
+ for (Instruction* use : load_work_list) {
+ if (!ReplaceLoadedValue(var, use)) {
+ return false;
+ }
+ }
return true;
}
@@ -177,21 +203,36 @@
uint32_t ptr_type_id = var->type_id();
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
- "Variable should be a pointer to an array.");
- uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
- assert(arr_type_inst->opcode() == SpvOpTypeArray &&
- "Variable should be a pointer to an array.");
+ "Variable should be a pointer to an array or structure.");
+ uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+ const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+ const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+ assert((is_array || is_struct) &&
+ "Variable should be a pointer to an array or structure.");
- uint32_t array_len_id = arr_type_inst->GetSingleWordInOperand(1);
- const analysis::Constant* array_len_const =
- context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
- assert(array_len_const != nullptr && "Array length must be a constant.");
- uint32_t array_len = array_len_const->GetU32();
+ // For arrays, each array element should be replaced with a new replacement
+ // variable
+ if (is_array) {
+ uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
+ const analysis::Constant* array_len_const =
+ context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
+ assert(array_len_const != nullptr && "Array length must be a constant.");
+ uint32_t array_len = array_len_const->GetU32();
- replacement_vars = replacement_variables_
- .insert({var, std::vector<uint32_t>(array_len, 0)})
- .first;
+ replacement_vars = replacement_variables_
+ .insert({var, std::vector<uint32_t>(array_len, 0)})
+ .first;
+ }
+ // For structures, each member should be replaced with a new replacement
+ // variable
+ if (is_struct) {
+ const uint32_t num_members = pointee_type_inst->NumInOperands();
+ replacement_vars =
+ replacement_variables_
+ .insert({var, std::vector<uint32_t>(num_members, 0)})
+ .first;
+ }
}
if (replacement_vars->second[idx] == 0) {
@@ -212,12 +253,17 @@
uint32_t ptr_type_id = var->type_id();
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
- "Variable should be a pointer to an array.");
- uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
- Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
- assert(arr_type_inst->opcode() == SpvOpTypeArray &&
- "Variable should be a pointer to an array.");
- uint32_t element_type_id = arr_type_inst->GetSingleWordInOperand(0);
+ "Variable should be a pointer to an array or structure.");
+ uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+ Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+ const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+ const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+ assert((is_array || is_struct) &&
+ "Variable should be a pointer to an array or structure.");
+
+ uint32_t element_type_id =
+ is_array ? pointee_type_inst->GetSingleWordInOperand(0)
+ : pointee_type_inst->GetSingleWordInOperand(idx);
uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType(
element_type_id, storage_class);
@@ -242,19 +288,42 @@
uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
if (decoration == SpvDecorationBinding) {
- uint32_t new_binding = new_decoration->GetSingleWordInOperand(2) + idx;
+ uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
+ if (is_array) {
+ new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
+ }
+ if (is_struct) {
+ // The binding offset that should be added is the sum of binding numbers
+ // used by previous members of the current struct.
+ for (uint32_t i = 0; i < idx; ++i) {
+ new_binding += GetNumBindingsUsedByType(
+ pointee_type_inst->GetSingleWordInOperand(i));
+ }
+ }
new_decoration->SetInOperand(2, {new_binding});
}
context()->AddAnnotationInst(std::move(new_decoration));
}
// Create a new OpName for the replacement variable.
+ std::vector<std::unique_ptr<Instruction>> names_to_add;
for (auto p : context()->GetNames(var->result_id())) {
Instruction* name_inst = p.second;
std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
- name_str += "[";
- name_str += utils::ToString(idx);
- name_str += "]";
+ if (is_array) {
+ name_str += "[" + utils::ToString(idx) + "]";
+ }
+ if (is_struct) {
+ Instruction* member_name_inst =
+ context()->GetMemberName(pointee_type_inst->result_id(), idx);
+ name_str += ".";
+ if (member_name_inst)
+ name_str += utils::MakeString(member_name_inst->GetOperand(2).words);
+ else
+ // In case the member does not have a name assigned to it, use the
+ // member index.
+ name_str += utils::ToString(idx);
+ }
std::unique_ptr<Instruction> new_name(new Instruction(
context(), SpvOpName, 0, 0,
@@ -262,12 +331,118 @@
{SPV_OPERAND_TYPE_ID, {id}},
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
Instruction* new_name_inst = new_name.get();
- context()->AddDebug2Inst(std::move(new_name));
get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
+ names_to_add.push_back(std::move(new_name));
}
+ // We shouldn't add the new names when we are iterating over name ranges
+ // above. We can add all the new names now.
+ for (auto& new_name : names_to_add)
+ context()->AddDebug2Inst(std::move(new_name));
+
return id;
}
+uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
+ uint32_t type_id) {
+ Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+
+ // If it's a pointer, look at the underlying type.
+ if (type_inst->opcode() == SpvOpTypePointer) {
+ type_id = type_inst->GetSingleWordInOperand(1);
+ type_inst = get_def_use_mgr()->GetDef(type_id);
+ }
+
+ // Arrays consume N*M binding numbers where N is the array length, and M is
+ // the number of bindings used by each array element.
+ if (type_inst->opcode() == SpvOpTypeArray) {
+ uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
+ uint32_t length_id = type_inst->GetSingleWordInOperand(1);
+ const analysis::Constant* length_const =
+ context()->get_constant_mgr()->FindDeclaredConstant(length_id);
+ // OpTypeArray's length must always be a constant
+ assert(length_const != nullptr);
+ uint32_t num_elems = length_const->GetU32();
+ return num_elems * GetNumBindingsUsedByType(element_type_id);
+ }
+
+ // The number of bindings consumed by a structure is the sum of the bindings
+ // used by its members.
+ if (type_inst->opcode() == SpvOpTypeStruct) {
+ uint32_t sum = 0;
+ for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
+ sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
+ return sum;
+ }
+
+ // All other types are considered to take up 1 binding number.
+ return 1;
+}
+
+bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var,
+ Instruction* value) {
+ // |var| is the global variable that has to be eliminated (OpVariable).
+ // |value| is the OpLoad instruction that has loaded |var|.
+ // The function expects all users of |value| to be OpCompositeExtract
+ // instructions. Otherwise the function returns false with an error message.
+ assert(value->opcode() == SpvOpLoad);
+ assert(value->GetSingleWordInOperand(0) == var->result_id());
+ std::vector<Instruction*> work_list;
+ bool failed = !get_def_use_mgr()->WhileEachUser(
+ value->result_id(), [this, &work_list](Instruction* use) {
+ if (use->opcode() != SpvOpCompositeExtract) {
+ context()->EmitErrorMessage(
+ "Variable cannot be replaced: invalid instruction", use);
+ return false;
+ }
+ work_list.push_back(use);
+ return true;
+ });
+
+ if (failed) {
+ return false;
+ }
+
+ for (Instruction* use : work_list) {
+ if (!ReplaceCompositeExtract(var, use)) {
+ return false;
+ }
+ }
+
+ // All usages of the loaded value have been killed. We can kill the OpLoad.
+ context()->KillInst(value);
+ return true;
+}
+
+bool DescriptorScalarReplacement::ReplaceCompositeExtract(
+ Instruction* var, Instruction* extract) {
+ assert(extract->opcode() == SpvOpCompositeExtract);
+ // We're currently only supporting extractions of one index at a time. If we
+ // need to, we can handle cases with multiple indexes in the future.
+ if (extract->NumInOperands() != 2) {
+ context()->EmitErrorMessage(
+ "Variable cannot be replaced: invalid instruction", extract);
+ return false;
+ }
+
+ uint32_t replacement_var =
+ GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
+
+ // The result type of the OpLoad is the same as the result type of the
+ // OpCompositeExtract.
+ uint32_t load_id = TakeNextId();
+ std::unique_ptr<Instruction> load(
+ new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
+ std::initializer_list<Operand>{
+ {SPV_OPERAND_TYPE_ID, {replacement_var}}}));
+ Instruction* load_instr = load.get();
+ get_def_use_mgr()->AnalyzeInstDefUse(load_instr);
+ context()->set_instr_block(load_instr, context()->get_instr_block(extract));
+ extract->InsertBefore(std::move(load));
+ context()->ReplaceAllUsesWith(extract->result_id(), load_id);
+ context()->KillInst(extract);
+ return true;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/desc_sroa.h b/third_party/SPIRV-Tools/source/opt/desc_sroa.h
index a95c6b5..c3aa0ea 100644
--- a/third_party/SPIRV-Tools/source/opt/desc_sroa.h
+++ b/third_party/SPIRV-Tools/source/opt/desc_sroa.h
@@ -61,6 +61,20 @@
// |true| if successful.
bool ReplaceAccessChain(Instruction* var, Instruction* use);
+ // Replaces the given compososite variable |var| loaded by OpLoad |value| with
+ // replacement variables, one for each component that's accessed in the
+ // shader. Assumes that |value| is only used by OpCompositeExtract
+ // instructions, one index at a time. Returns true on success, and false
+ // otherwise.
+ bool ReplaceLoadedValue(Instruction* var, Instruction* value);
+
+ // Replaces the given OpCompositeExtract |extract| and all of its references
+ // with an OpLoad of a replacement variable. |var| is the variable with
+ // composite type whose value is being used by |extract|. Assumes that
+ // |extract| is extracting one index only. Returns true on success, and false
+ // otherwise.
+ bool ReplaceCompositeExtract(Instruction* var, Instruction* extract);
+
// Returns the id of the variable that will be used to replace the |idx|th
// element of |var|. The variable is created if it has not already been
// created.
@@ -70,6 +84,15 @@
// element of |var|.
uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx);
+ // Returns the number of bindings used by the given |type_id|.
+ // All types are considered to use 1 binding slot, except:
+ // 1- A pointer type consumes as many binding numbers as its pointee.
+ // 2- An array of size N consumes N*M binding numbers, where M is the number
+ // of bindings used by each array element.
+ // 3- The number of bindings consumed by a structure is the sum of the
+ // bindings used by its members.
+ uint32_t GetNumBindingsUsedByType(uint32_t type_id);
+
// A map from an OpVariable instruction to the set of variables that will be
// used to replace it. The entry |replacement_variables_[var][i]| is the id of
// a variable that will be used in the place of the the ith element of the
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp b/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
index aef43e6..b692d26 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_analysis.cpp
@@ -55,12 +55,25 @@
return tree_.Dominates(bb_a, bb_b);
}
- Instruction* current_inst = a;
- while ((current_inst = current_inst->NextNode())) {
- if (current_inst == b) {
+ const Instruction* current = a;
+ const Instruction* other = b;
+
+ if (tree_.IsPostDominator()) {
+ std::swap(current, other);
+ }
+
+ // We handle OpLabel instructions explicitly since they are not stored in the
+ // instruction list.
+ if (current->opcode() == SpvOpLabel) {
+ return true;
+ }
+
+ while ((current = current->NextNode())) {
+ if (current == other) {
return true;
}
}
+
return false;
}
diff --git a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
index da5073a..7e61506 100644
--- a/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
+++ b/third_party/SPIRV-Tools/source/opt/dominator_tree.cpp
@@ -176,7 +176,8 @@
// The tree construction requires 1 entry point, so we add a dummy node
// that is connected to all function exiting basic blocks.
// An exiting basic block is a block with an OpKill, OpUnreachable,
- // OpReturn or OpReturnValue as terminator instruction.
+ // OpReturn, OpReturnValue, or OpTerminateInvocation as terminator
+ // instruction.
for (BasicBlock& bb : f) {
if (bb.hasSuccessor()) {
BasicBlockListTy& pred_list = predecessors_[&bb];
diff --git a/third_party/SPIRV-Tools/source/opt/function.h b/third_party/SPIRV-Tools/source/opt/function.h
index d569bf9..f5035f0 100644
--- a/third_party/SPIRV-Tools/source/opt/function.h
+++ b/third_party/SPIRV-Tools/source/opt/function.h
@@ -72,6 +72,10 @@
// Delete all basic blocks that contain no instructions.
inline void RemoveEmptyBlocks();
+ // Removes a parameter from the function with result id equal to |id|.
+ // Does nothing if the function doesn't have such a parameter.
+ inline void RemoveParameter(uint32_t id);
+
// Saves the given function end instruction.
inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
@@ -219,6 +223,14 @@
blocks_.erase(first_empty, std::end(blocks_));
}
+inline void Function::RemoveParameter(uint32_t id) {
+ params_.erase(std::remove_if(params_.begin(), params_.end(),
+ [id](const std::unique_ptr<Instruction>& param) {
+ return param->result_id() == id;
+ }),
+ params_.end());
+}
+
inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
end_inst_ = std::move(end_inst);
}
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
index cb5a126..ef94d0d 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.cpp
@@ -384,7 +384,8 @@
for (auto callee_block_itr = calleeFn->begin();
callee_block_itr != calleeFn->end(); ++callee_block_itr) {
if (callee_block_itr->tail()->opcode() == SpvOpUnreachable ||
- callee_block_itr->tail()->opcode() == SpvOpKill) {
+ callee_block_itr->tail()->opcode() == SpvOpKill ||
+ callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) {
returnLabelId = context()->TakeNextId();
break;
}
@@ -738,16 +739,18 @@
bool func_is_called_from_continue =
funcs_called_from_continue_.count(func->result_id()) != 0;
- if (func_is_called_from_continue && ContainsKill(func)) {
+ if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) {
return false;
}
return true;
}
-bool InlinePass::ContainsKill(Function* func) const {
- return !func->WhileEachInst(
- [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const {
+ return !func->WhileEachInst([](Instruction* inst) {
+ const auto opcode = inst->opcode();
+ return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation);
+ });
}
void InlinePass::InitializeInline() {
diff --git a/third_party/SPIRV-Tools/source/opt/inline_pass.h b/third_party/SPIRV-Tools/source/opt/inline_pass.h
index 202bc97..abe773a 100644
--- a/third_party/SPIRV-Tools/source/opt/inline_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/inline_pass.h
@@ -139,8 +139,9 @@
// Return true if |func| is a function that can be inlined.
bool IsInlinableFunction(Function* func);
- // Returns true if |func| contains an OpKill instruction.
- bool ContainsKill(Function* func) const;
+ // Returns true if |func| contains an OpKill or OpTerminateInvocation
+ // instruction.
+ bool ContainsKillOrTerminateInvocation(Function* func) const;
// Update phis in succeeding blocks to point to new last block
void UpdateSucceedingPhis(
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index 7d8fed8..e99a5ae 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -306,6 +306,10 @@
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
}
+ // Insert an operand before the |index|-th operand
+ void InsertOperand(uint32_t index, Operand&& operand) {
+ operands_.insert(operands_.begin() + index, operand);
+ }
// The following methods are similar to the above, but are for in operands.
uint32_t NumInOperands() const {
@@ -535,6 +539,11 @@
// OpenCLDebugInfo100InstructionsMax.
OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
+ // Returns true if it is an OpenCL.DebugInfo.100 instruction.
+ bool IsOpenCL100DebugInstr() const {
+ return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
+ }
+
// Dump this instruction on stderr. Useful when running interactive
// debuggers.
void Dump() const;
diff --git a/third_party/SPIRV-Tools/source/opt/instruction_list.h b/third_party/SPIRV-Tools/source/opt/instruction_list.h
index ea1cc7c..417cbd7 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction_list.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction_list.h
@@ -61,6 +61,16 @@
: utils::IntrusiveList<Instruction>::iterator(i) {}
iterator(Instruction* i) : utils::IntrusiveList<Instruction>::iterator(i) {}
+ iterator& operator++() {
+ utils::IntrusiveList<Instruction>::iterator::operator++();
+ return *this;
+ }
+
+ iterator& operator--() {
+ utils::IntrusiveList<Instruction>::iterator::operator--();
+ return *this;
+ }
+
// DEPRECATED: Please use MoveBefore with an InstructionList instead.
//
// Moves the nodes in |list| to the list that |this| points to. The
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index 4210ad5..e6a55a8 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -174,7 +174,9 @@
context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
kInstVertOutInstanceIndex, base_offset_id, builder);
} break;
- case SpvExecutionModelGLCompute: {
+ case SpvExecutionModelGLCompute:
+ case SpvExecutionModelTaskNV:
+ case SpvExecutionModelMeshNV: {
// Load and store GlobalInvocationId.
uint32_t load_id = GenVarLoad(
context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
@@ -914,6 +916,7 @@
stage != SpvExecutionModelGLCompute &&
stage != SpvExecutionModelTessellationControl &&
stage != SpvExecutionModelTessellationEvaluation &&
+ stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV &&
stage != SpvExecutionModelRayGenerationNV &&
stage != SpvExecutionModelIntersectionNV &&
stage != SpvExecutionModelAnyHitNV &&
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
index df04066..0791097 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -97,8 +97,9 @@
}
void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
- // The ConstantManager contains Type pointers. If the TypeManager goes
- // away, the ConstantManager has to go away.
+ // The ConstantManager and DebugInfoManager contain Type pointers. If the
+ // TypeManager goes away, the ConstantManager and DebugInfoManager have to
+ // go away.
if (analyses_to_invalidate & kAnalysisTypes) {
analyses_to_invalidate |= kAnalysisConstants;
analyses_to_invalidate |= kAnalysisDebugInfo;
@@ -179,6 +180,9 @@
decoration_mgr_->RemoveDecoration(inst);
}
}
+ if (AreAnalysesValid(kAnalysisDebugInfo)) {
+ get_debug_info_mgr()->ClearDebugInfo(inst);
+ }
if (type_mgr_ && IsTypeInst(inst->opcode())) {
type_mgr_->RemoveId(inst->result_id());
}
@@ -218,6 +222,13 @@
return false;
}
+void IRContext::KillDebugDeclareInsts(Function* fn) {
+ fn->ForEachInst([this](Instruction* inst) {
+ if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
+ KillInst(inst);
+ });
+}
+
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
return ReplaceAllUsesWithPredicate(
before, after, [](Instruction*, uint32_t) { return true; });
@@ -344,6 +355,9 @@
get_decoration_mgr()->RemoveDecoration(inst);
}
}
+ if (AreAnalysesValid(kAnalysisDebugInfo)) {
+ get_debug_info_mgr()->ClearDebugInfo(inst);
+ }
RemoveFromIdToName(inst);
}
@@ -356,6 +370,9 @@
get_decoration_mgr()->AddDecoration(inst);
}
}
+ if (AreAnalysesValid(kAnalysisDebugInfo)) {
+ get_debug_info_mgr()->AnalyzeDebugInst(inst);
+ }
if (id_to_name_ &&
(inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
@@ -394,6 +411,7 @@
if (operand.words[0] == id) {
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+ get_def_use_mgr()->AnalyzeInstUse(&*it);
}
}
}
@@ -408,13 +426,10 @@
if (operand.words[0] == id) {
operand.words[0] =
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+ get_def_use_mgr()->AnalyzeInstUse(&*it);
}
}
}
- // Notice that we do not need anythings to do for local variables.
- // DebugLocalVariable does not have an OpVariable operand. Instead,
- // DebugDeclare/DebugValue has an OpVariable operand for a local
- // variable. The function inlining pass handles it properly.
}
void IRContext::AddCombinatorsForCapability(uint32_t capability) {
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.h b/third_party/SPIRV-Tools/source/opt/ir_context.h
index a1b63ff..37be836 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.h
@@ -357,6 +357,13 @@
inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator>
GetNames(uint32_t id);
+ // Returns an OpMemberName instruction that targets |struct_type_id| at
+ // index |index|. Returns nullptr if no such instruction exists.
+ // While the SPIR-V spec does not prohibit having multiple OpMemberName
+ // instructions for the same structure member, it is hard to imagine a member
+ // having more than one name. This method returns the first one it finds.
+ inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
+
// Sets the message consumer to the given |consumer|. |consumer| which will be
// invoked every time there is a message to be communicated to the outside.
void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -396,6 +403,9 @@
// instruction exists.
Instruction* KillInst(Instruction* inst);
+ // Deletes DebugDeclare instructions in the given function |fn|.
+ void KillDebugDeclareInsts(Function* fn);
+
// Returns true if all of the given analyses are valid.
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
@@ -1061,7 +1071,9 @@
void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
if (AreAnalysesValid(kAnalysisNameMap)) {
if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
- id_to_name_->insert({d->result_id(), d.get()});
+ // OpName and OpMemberName do not have result-ids. The target of the
+ // instruction is at InOperand index 0.
+ id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
}
}
module()->AddDebug2Inst(std::move(d));
@@ -1135,6 +1147,21 @@
return make_range(std::move(result.first), std::move(result.second));
}
+Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) {
+ if (!AreAnalysesValid(kAnalysisNameMap)) {
+ BuildIdToNameMap();
+ }
+ auto result = id_to_name_->equal_range(struct_type_id);
+ for (auto i = result.first; i != result.second; ++i) {
+ auto* name_instr = i->second;
+ if (name_instr->opcode() == SpvOpMemberName &&
+ name_instr->GetSingleWordInOperand(1) == index) {
+ return name_instr;
+ }
+ }
+ return nullptr;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
index 0afe798..9b8c112 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
@@ -281,7 +281,7 @@
// Initialize collections
supported_ref_ptrs_.clear();
- // Initialize extension whitelist
+ // Initialize extension allowlist
InitExtensions();
}
@@ -292,11 +292,11 @@
if (context()->get_feature_mgr()->HasCapability(
SpvCapabilityVariablePointers))
return false;
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -336,8 +336,8 @@
}
void LocalAccessChainConvertPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
@@ -382,6 +382,7 @@
"SPV_KHR_ray_tracing",
"SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
+ "SPV_KHR_terminate_invocation",
});
}
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
index e3592bf..552062e 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.h
@@ -110,7 +110,7 @@
// Returns a status to indicate success or failure, and change or no change.
Status ConvertLocalAccessChains(Function* func);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are allowed by this pass.
@@ -124,7 +124,7 @@
std::unordered_set<uint32_t> supported_ref_ptrs_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
index b5435bb..bd5d751 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
@@ -31,6 +31,11 @@
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
+ auto dbg_op = user->GetOpenCL100DebugOpcode();
+ if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+ dbg_op == OpenCLDebugInfo100DebugValue) {
+ return true;
+ }
SpvOp op = user->opcode();
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
if (!HasOnlySupportedRefs(user->result_id())) {
@@ -73,10 +78,12 @@
// variable.
if (ptrInst->opcode() == SpvOpVariable) {
// If a previous store to same variable, mark the store
- // for deletion if not still used.
+ // for deletion if not still used. Don't delete store
+ // if debugging; let ssa-rewrite and DCE handle it
auto prev_store = var2store_.find(varId);
if (prev_store != var2store_.end() &&
- instructions_to_save.count(prev_store->second) == 0) {
+ instructions_to_save.count(prev_store->second) == 0 &&
+ !context()->get_debug_info_mgr()->IsDebugDeclared(varId)) {
instructions_to_kill.push_back(prev_store->second);
modified = true;
}
@@ -168,16 +175,16 @@
// Clear collections
supported_ref_ptrs_.clear();
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
InitExtensions();
}
bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -214,8 +221,8 @@
}
void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
- extensions_whitelist_.clear();
- extensions_whitelist_.insert({
+ extensions_allowlist_.clear();
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
@@ -260,6 +267,7 @@
"SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
+ "SPV_KHR_terminate_invocation",
});
}
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
index 0fe7732..ea72816 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.h
@@ -63,7 +63,7 @@
// where possible. Assumes logical addressing.
bool LocalSingleBlockLoadStoreElim(Function* func);
- // Initialize extensions whitelist
+ // Initialize extensions allowlist
void InitExtensions();
// Return true if all extensions in this module are supported by this pass.
@@ -94,7 +94,7 @@
std::unordered_set<uint32_t> pinned_vars_;
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
// Variables that are only referenced by supported operations for this
// pass ie. loads and stores.
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
index 4c71ce1..2384107 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
@@ -46,11 +46,11 @@
}
bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
- // If any extension not in whitelist, return false
+ // If any extension not in allowlist, return false
for (auto& ei : get_module()->extensions()) {
const char* extName =
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
- if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
+ if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
return false;
}
return true;
@@ -74,12 +74,12 @@
LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default;
Pass::Status LocalSingleStoreElimPass::Process() {
- InitExtensionWhiteList();
+ InitExtensionAllowList();
return ProcessImpl();
}
-void LocalSingleStoreElimPass::InitExtensionWhiteList() {
- extensions_whitelist_.insert({
+void LocalSingleStoreElimPass::InitExtensionAllowList() {
+ extensions_allowlist_.insert({
"SPV_AMD_shader_explicit_vertex_parameter",
"SPV_AMD_shader_trinary_minmax",
"SPV_AMD_gcn_shader",
@@ -121,6 +121,7 @@
"SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
+ "SPV_KHR_terminate_invocation",
});
}
bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
@@ -133,7 +134,27 @@
return false;
}
- return RewriteLoads(store_inst, users);
+ bool all_rewritten;
+ bool modified = RewriteLoads(store_inst, users, &all_rewritten);
+
+ // If all uses are rewritten and the variable has a DebugDeclare and the
+ // variable is not an aggregate, add a DebugValue after the store and remove
+ // the DebugDeclare.
+ uint32_t var_id = var_inst->result_id();
+ if (all_rewritten &&
+ context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
+ const analysis::Type* var_type =
+ context()->get_type_mgr()->GetType(var_inst->type_id());
+ const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
+ if (!(store_type->AsStruct() || store_type->AsArray())) {
+ context()->get_debug_info_mgr()->AddDebugValue(
+ store_inst, var_id, store_inst->GetSingleWordInOperand(1),
+ store_inst);
+ context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+ }
+ }
+
+ return modified;
}
Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
@@ -172,6 +193,14 @@
case SpvOpName:
case SpvOpCopyObject:
break;
+ case SpvOpExtInst: {
+ auto dbg_op = user->GetOpenCL100DebugOpcode();
+ if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+ dbg_op == OpenCLDebugInfo100DebugValue) {
+ break;
+ }
+ return nullptr;
+ }
default:
if (!user->IsDecoration()) {
// Don't know if this instruction modifies the variable.
@@ -218,7 +247,8 @@
}
bool LocalSingleStoreElimPass::RewriteLoads(
- Instruction* store_inst, const std::vector<Instruction*>& uses) {
+ Instruction* store_inst, const std::vector<Instruction*>& uses,
+ bool* all_rewritten) {
BasicBlock* store_block = context()->get_instr_block(store_inst);
DominatorAnalysis* dominator_analysis =
context()->GetDominatorAnalysis(store_block->GetParent());
@@ -229,16 +259,22 @@
else
stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
- std::vector<Instruction*> uses_in_store_block;
+ *all_rewritten = true;
bool modified = false;
for (Instruction* use : uses) {
- if (use->opcode() == SpvOpLoad) {
- if (dominator_analysis->Dominates(store_inst, use)) {
- modified = true;
- context()->KillNamesAndDecorates(use->result_id());
- context()->ReplaceAllUsesWith(use->result_id(), stored_id);
- context()->KillInst(use);
- }
+ if (use->opcode() == SpvOpStore) continue;
+ auto dbg_op = use->GetOpenCL100DebugOpcode();
+ if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+ dbg_op == OpenCLDebugInfo100DebugValue)
+ continue;
+ if (use->opcode() == SpvOpLoad &&
+ dominator_analysis->Dominates(store_inst, use)) {
+ modified = true;
+ context()->KillNamesAndDecorates(use->result_id());
+ context()->ReplaceAllUsesWith(use->result_id(), stored_id);
+ context()->KillInst(use);
+ } else {
+ *all_rewritten = false;
}
}
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
index 4cf8bbb..d66a441 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.h
@@ -57,8 +57,8 @@
// any resulting dead code.
bool LocalSingleStoreElim(Function* func);
- // Initialize extensions whitelist
- void InitExtensionWhiteList();
+ // Initialize extensions allowlist
+ void InitExtensionAllowList();
// Return true if all extensions in this module are allowed by this pass.
bool AllExtensionsSupported() const;
@@ -89,12 +89,13 @@
bool FeedsAStore(Instruction* inst) const;
// Replaces all of the loads in |uses| by the value stored in |store_inst|.
- // The load instructions are then killed.
+ // The load instructions are then killed. |all_rewritten| is true iff all
+ // uses have been rewritten.
bool RewriteLoads(Instruction* store_inst,
- const std::vector<Instruction*>& uses);
+ const std::vector<Instruction*>& uses, bool* all_rewritten);
// Extensions supported by this pass.
- std::unordered_set<std::string> extensions_whitelist_;
+ std::unordered_set<std::string> extensions_allowlist_;
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
index 11f7e9c..ed0dd28 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_descriptor.cpp
@@ -485,10 +485,27 @@
if (include_pre_header && GetPreHeaderBlock())
ordered_loop_blocks->push_back(loop_preheader_);
- cfg.ForEachBlockInReversePostOrder(
- loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
- if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
- });
+
+ bool is_shader =
+ context_->get_feature_mgr()->HasCapability(SpvCapabilityShader);
+ if (!is_shader) {
+ cfg.ForEachBlockInReversePostOrder(
+ loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) {
+ if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb);
+ });
+ } else {
+ // If this is a shader, it is possible that there are unreachable merge and
+ // continue blocks that must be copied to retain the structured order.
+ // The structured order will include these.
+ std::list<BasicBlock*> order;
+ cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+ for (BasicBlock* bb : order) {
+ if (bb == GetMergeBlock()) {
+ break;
+ }
+ ordered_loop_blocks->push_back(bb);
+ }
+ }
if (include_merge && GetMergeBlock())
ordered_loop_blocks->push_back(loop_merge_);
}
diff --git a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
index 10fac04..40cf6bc 100644
--- a/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
+++ b/third_party/SPIRV-Tools/source/opt/loop_unroller.cpp
@@ -997,7 +997,8 @@
const BasicBlock* block = context_->cfg()->block(label_id);
if (block->ctail()->opcode() == SpvOp::SpvOpKill ||
block->ctail()->opcode() == SpvOp::SpvOpReturn ||
- block->ctail()->opcode() == SpvOp::SpvOpReturnValue) {
+ block->ctail()->opcode() == SpvOp::SpvOpReturnValue ||
+ block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) {
return false;
}
}
diff --git a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
index d23d679..5738798 100644
--- a/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/mem_pass.cpp
@@ -20,6 +20,7 @@
#include <set>
#include <vector>
+#include "OpenCLDebugInfo100.h"
#include "source/cfa.h"
#include "source/opt/basic_block.h"
#include "source/opt/dominator_analysis.h"
@@ -225,6 +226,11 @@
bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
+ auto dbg_op = user->GetOpenCL100DebugOpcode();
+ if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+ dbg_op == OpenCLDebugInfo100DebugValue) {
+ return true;
+ }
SpvOp op = user->opcode();
if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
!IsNonTypeDecorate(op)) {
diff --git a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
index 8cb4299..2421c2c 100644
--- a/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/merge_return_pass.cpp
@@ -299,9 +299,6 @@
// There is at least one values that needs to be replaced.
// First create the OpPhi instruction.
- InstructionBuilder builder(
- context(), &*merge_block->begin(),
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
uint32_t undef_id = Type2Undef(inst.type_id());
std::vector<uint32_t> phi_operands;
const std::set<uint32_t>& new_edges = new_edges_[merge_block];
@@ -318,7 +315,50 @@
phi_operands.push_back(pred_id);
}
- Instruction* new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+ Instruction* new_phi = nullptr;
+ // If the instruction is a pointer and variable pointers are not an option,
+ // then we have to regenerate the instruction instead of creating an OpPhi
+ // instruction. If not, the Spir-V will be invalid.
+ Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id());
+ bool regenerateInstruction = false;
+ if (inst_type->opcode() == SpvOpTypePointer) {
+ if (!context()->get_feature_mgr()->HasCapability(
+ SpvCapabilityVariablePointers)) {
+ regenerateInstruction = true;
+ }
+
+ uint32_t storage_class = inst_type->GetSingleWordInOperand(0);
+ if (storage_class != SpvStorageClassWorkgroup &&
+ storage_class != SpvStorageClassStorageBuffer) {
+ regenerateInstruction = true;
+ }
+ }
+
+ if (regenerateInstruction) {
+ std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
+ uint32_t new_id = TakeNextId();
+ regen_inst->SetResultId(new_id);
+ Instruction* insert_pos = &*merge_block->begin();
+ while (insert_pos->opcode() == SpvOpPhi) {
+ insert_pos = insert_pos->NextNode();
+ }
+ new_phi = insert_pos->InsertBefore(std::move(regen_inst));
+ get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
+ context()->set_instr_block(new_phi, merge_block);
+
+ new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
+ Instruction* use = get_def_use_mgr()->GetDef(*use_id);
+ BasicBlock* use_bb = context()->get_instr_block(use);
+ if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
+ CreatePhiNodesForInst(merge_block, *use);
+ }
+ });
+ } else {
+ InstructionBuilder builder(
+ context(), &*merge_block->begin(),
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+ new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+ }
uint32_t result_of_phi = new_phi->result_id();
// Update all of the users to use the result of the new OpPhi.
diff --git a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
index 6df690d..dd6cbbd 100644
--- a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.cpp
@@ -138,7 +138,7 @@
function->begin()->begin()->InsertBefore(move(var));
// Update uses where the type may have changed.
- return UpdateUses(variable->result_id());
+ return UpdateUses(variable);
}
uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@@ -157,6 +157,10 @@
bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
// The cases in this switch have to match the cases in |UpdateUse|.
// If we don't know how to update it, it is not valid.
+ if (inst->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugGlobalVariable) {
+ return true;
+ }
switch (inst->opcode()) {
case SpvOpLoad:
case SpvOpStore:
@@ -175,10 +179,16 @@
}
}
-bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
+bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
// The cases in this switch have to match the cases in |IsValidUse|. If we
// don't think it is valid, the optimization will not view the variable as a
// candidate, and therefore the use will not be updated.
+ if (inst->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugGlobalVariable) {
+ context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
+ user);
+ return true;
+ }
switch (inst->opcode()) {
case SpvOpLoad:
case SpvOpStore:
@@ -196,7 +206,7 @@
context()->AnalyzeUses(inst);
// Update uses where the type may have changed.
- if (!UpdateUses(inst->result_id())) {
+ if (!UpdateUses(inst)) {
return false;
}
} break;
@@ -211,13 +221,14 @@
return true;
}
-bool PrivateToLocalPass::UpdateUses(uint32_t id) {
+bool PrivateToLocalPass::UpdateUses(Instruction* inst) {
+ uint32_t id = inst->result_id();
std::vector<Instruction*> uses;
context()->get_def_use_mgr()->ForEachUser(
id, [&uses](Instruction* use) { uses.push_back(use); });
for (Instruction* use : uses) {
- if (!UpdateUse(use)) {
+ if (!UpdateUse(use, inst)) {
return false;
}
}
diff --git a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
index 3f9135c0..c6127d6 100644
--- a/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/private_to_local_pass.h
@@ -63,8 +63,8 @@
// Updates |inst|, and any instruction dependent on |inst|, to reflect the
// change of the base pointer now pointing to the function storage class.
- bool UpdateUse(Instruction* inst);
- bool UpdateUses(uint32_t id);
+ bool UpdateUse(Instruction* inst, Instruction* user);
+ bool UpdateUses(Instruction* inst);
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
index 7b5a015..c58adf4 100644
--- a/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
+++ b/third_party/SPIRV-Tools/source/opt/reduce_load_size.cpp
@@ -112,10 +112,10 @@
Instruction* new_access_chain = ir_builder.AddAccessChain(
pointer_to_result_type_id,
composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
- Instruction* new_laod =
+ Instruction* new_load =
ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
- context()->ReplaceAllUsesWith(inst->result_id(), new_laod->result_id());
+ context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id());
context()->KillInst(inst);
return true;
}
@@ -139,6 +139,7 @@
all_elements_used =
!def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
+ if (use->IsOpenCL100DebugInstr()) return true;
if (use->opcode() != SpvOpCompositeExtract ||
use->NumInOperands() == 1) {
return false;
diff --git a/third_party/SPIRV-Tools/source/opt/reflect.h b/third_party/SPIRV-Tools/source/opt/reflect.h
index 51d23a7..2e253ad 100644
--- a/third_party/SPIRV-Tools/source/opt/reflect.h
+++ b/third_party/SPIRV-Tools/source/opt/reflect.h
@@ -60,7 +60,8 @@
return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp;
}
inline bool IsTerminatorInst(SpvOp opcode) {
- return opcode >= SpvOpBranch && opcode <= SpvOpUnreachable;
+ return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) ||
+ (opcode == SpvOpTerminateInvocation);
}
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp b/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
index 4e0f24f..38b7539 100644
--- a/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
+++ b/third_party/SPIRV-Tools/source/opt/replace_invalid_opc.cpp
@@ -141,6 +141,7 @@
// TODO: Teach |ReplaceInstruction| to handle block terminators. Then
// uncomment the OpKill case.
// case SpvOpKill:
+ // case SpvOpTerminateInstruction:
return true;
default:
return false;
diff --git a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
index 69c3a1f..1477db4 100644
--- a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.cpp
@@ -307,6 +307,8 @@
}
if (pass_->IsTargetVar(var_id)) {
WriteVariable(var_id, bb, val_id);
+ pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
+ inst);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -437,6 +439,8 @@
// Add Phi instructions from completed Phi candidates.
std::vector<Instruction*> generated_phis;
+ // Add DebugValue instructions for Phi instructions.
+ std::vector<Instruction*> dbg_values_for_phis;
for (const PhiCandidate* phi_candidate : phis_to_generate_) {
#if SSA_REWRITE_DEBUGGING_LEVEL > 2
std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
@@ -447,9 +451,10 @@
"Tried to instantiate a Phi instruction from an incomplete Phi "
"candidate");
+ auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
+
// Build the vector of operands for the new OpPhi instruction.
- uint32_t type_id = pass_->GetPointeeTypeId(
- pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
+ uint32_t type_id = pass_->GetPointeeTypeId(local_var);
std::vector<Operand> phi_operands;
uint32_t arg_ix = 0;
std::unordered_map<uint32_t, uint32_t> already_seen;
@@ -479,11 +484,17 @@
pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
auto insert_it = phi_candidate->bb()->begin();
- insert_it.InsertBefore(std::move(phi_inst));
+ insert_it = insert_it.InsertBefore(std::move(phi_inst));
pass_->context()->get_decoration_mgr()->CloneDecorations(
phi_candidate->var_id(), phi_candidate->result_id(),
{SpvDecorationRelaxedPrecision});
+ // Add DebugValue for the new OpPhi instruction.
+ insert_it->SetDebugScope(local_var->GetDebugScope());
+ pass_->context()->get_debug_info_mgr()->AddDebugValue(
+ &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
+ &*insert_it);
+
modified = true;
}
@@ -604,6 +615,8 @@
<< fp->PrettyPrint(0) << "\n";
#endif
+ if (modified) pass_->context()->KillDebugDeclareInsts(fp);
+
return modified ? Pass::Status::SuccessWithChange
: Pass::Status::SuccessWithoutChange;
}
diff --git a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
index bbe89c8..bbbfebb 100644
--- a/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/ssa_rewrite_pass.h
@@ -39,8 +39,7 @@
// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
class SSARewriter {
public:
- SSARewriter(MemPass* pass)
- : pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
+ SSARewriter(MemPass* pass) : pass_(pass) {}
// Rewrites SSA-target variables in function |fp| into SSA. This is the
// entry point for the SSA rewrite algorithm. SSA-target variables are
@@ -287,10 +286,6 @@
// Memory pass requesting the SSA rewriter.
MemPass* pass_;
-
- // ID of the first Phi created by the SSA rewriter. During rewriting, any
- // ID bigger than this corresponds to a Phi candidate.
- uint32_t first_phi_id_;
};
class SSARewritePass : public MemPass {
diff --git a/third_party/SPIRV-Tools/source/opt/vector_dce.cpp b/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
index 92532e3..14a55c1 100644
--- a/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
+++ b/third_party/SPIRV-Tools/source/opt/vector_dce.cpp
@@ -52,6 +52,9 @@
// components are live because of arbitrary nesting of structs.
function->ForEachInst(
[&work_list, this, live_components](Instruction* current_inst) {
+ if (current_inst->IsOpenCL100DebugInstr()) {
+ return;
+ }
if (!HasVectorOrScalarResult(current_inst) ||
!context()->IsCombinatorInstruction(current_inst)) {
MarkUsesAsLive(current_inst, all_components_live_, live_components,
@@ -297,51 +300,60 @@
bool VectorDCE::RewriteInstructions(
Function* function, const VectorDCE::LiveComponentMap& live_components) {
bool modified = false;
- function->ForEachInst(
- [&modified, this, live_components](Instruction* current_inst) {
- if (!context()->IsCombinatorInstruction(current_inst)) {
- return;
- }
- auto live_component = live_components.find(current_inst->result_id());
- if (live_component == live_components.end()) {
- // If this instruction is not in live_components then it does not
- // produce a vector, or it is never referenced and ADCE will remove
- // it. No point in trying to differentiate.
- return;
- }
+ // Kill DebugValue in the middle of the instruction iteration will result
+ // in accessing a dangling pointer. We keep dead DebugValue instructions
+ // in |dead_dbg_value| to kill them once after the iteration.
+ std::vector<Instruction*> dead_dbg_value;
- // If no element in the current instruction is used replace it with an
- // OpUndef.
- if (live_component->second.Empty()) {
- modified = true;
- uint32_t undef_id = this->Type2Undef(current_inst->type_id());
- context()->KillNamesAndDecorates(current_inst);
- context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
- context()->KillInst(current_inst);
- return;
- }
+ function->ForEachInst([&modified, this, live_components,
+ &dead_dbg_value](Instruction* current_inst) {
+ if (!context()->IsCombinatorInstruction(current_inst)) {
+ return;
+ }
- switch (current_inst->opcode()) {
- case SpvOpCompositeInsert:
- modified |=
- RewriteInsertInstruction(current_inst, live_component->second);
- break;
- case SpvOpCompositeConstruct:
- // TODO: The members that are not live can be replaced by an undef
- // or constant. This will remove uses of those values, and possibly
- // create opportunities for ADCE.
- break;
- default:
- // Do nothing.
- break;
- }
- });
+ auto live_component = live_components.find(current_inst->result_id());
+ if (live_component == live_components.end()) {
+ // If this instruction is not in live_components then it does not
+ // produce a vector, or it is never referenced and ADCE will remove
+ // it. No point in trying to differentiate.
+ return;
+ }
+
+ // If no element in the current instruction is used replace it with an
+ // OpUndef.
+ if (live_component->second.Empty()) {
+ modified = true;
+ MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value);
+ uint32_t undef_id = this->Type2Undef(current_inst->type_id());
+ context()->KillNamesAndDecorates(current_inst);
+ context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
+ context()->KillInst(current_inst);
+ return;
+ }
+
+ switch (current_inst->opcode()) {
+ case SpvOpCompositeInsert:
+ modified |= RewriteInsertInstruction(
+ current_inst, live_component->second, &dead_dbg_value);
+ break;
+ case SpvOpCompositeConstruct:
+ // TODO: The members that are not live can be replaced by an undef
+ // or constant. This will remove uses of those values, and possibly
+ // create opportunities for ADCE.
+ break;
+ default:
+ // Do nothing.
+ break;
+ }
+ });
+ for (auto* i : dead_dbg_value) context()->KillInst(i);
return modified;
}
bool VectorDCE::RewriteInsertInstruction(
- Instruction* current_inst, const utils::BitVector& live_components) {
+ Instruction* current_inst, const utils::BitVector& live_components,
+ std::vector<Instruction*>* dead_dbg_value) {
// If the value being inserted is not live, then we can skip the insert.
if (current_inst->NumInOperands() == 2) {
@@ -355,6 +367,7 @@
uint32_t insert_index = current_inst->GetSingleWordInOperand(2);
if (!live_components.Get(insert_index)) {
+ MarkDebugValueUsesAsDead(current_inst, dead_dbg_value);
context()->KillNamesAndDecorates(current_inst->result_id());
uint32_t composite_id =
current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
@@ -377,6 +390,15 @@
return false;
}
+void VectorDCE::MarkDebugValueUsesAsDead(
+ Instruction* composite, std::vector<Instruction*>* dead_dbg_value) {
+ context()->get_def_use_mgr()->ForEachUser(
+ composite, [&dead_dbg_value](Instruction* use) {
+ if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue)
+ dead_dbg_value->push_back(use);
+ });
+}
+
void VectorDCE::AddItemToWorkListIfNeeded(
WorkListItem work_item, VectorDCE::LiveComponentMap* live_components,
std::vector<WorkListItem>* work_list) {
diff --git a/third_party/SPIRV-Tools/source/opt/vector_dce.h b/third_party/SPIRV-Tools/source/opt/vector_dce.h
index 4f039c5..0df9aee 100644
--- a/third_party/SPIRV-Tools/source/opt/vector_dce.h
+++ b/third_party/SPIRV-Tools/source/opt/vector_dce.h
@@ -73,6 +73,11 @@
bool RewriteInstructions(Function* function,
const LiveComponentMap& live_components);
+ // Makrs all DebugValue instructions that use |composite| for their values as
+ // dead instructions by putting them into |dead_dbg_value|.
+ void MarkDebugValueUsesAsDead(Instruction* composite,
+ std::vector<Instruction*>* dead_dbg_value);
+
// Rewrites the OpCompositeInsert instruction |current_inst| to avoid
// unnecessary computes given that the only components of the result that are
// live are |live_components|.
@@ -83,7 +88,8 @@
// If the composite input to |current_inst| is not live, then it is replaced
// by and OpUndef in |current_inst|.
bool RewriteInsertInstruction(Instruction* current_inst,
- const utils::BitVector& live_components);
+ const utils::BitVector& live_components,
+ std::vector<Instruction*>* dead_dbg_value);
// Returns true if the result of |inst| is a vector or a scalar.
bool HasVectorOrScalarResult(const Instruction* inst) const;
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
index 3c8bae6..4d70840 100644
--- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
+++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.cpp
@@ -27,7 +27,8 @@
for (uint32_t func_id : func_to_process) {
Function* func = context()->GetFunction(func_id);
bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
- if (inst->opcode() == SpvOpKill) {
+ const auto opcode = inst->opcode();
+ if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) {
modified = true;
if (!ReplaceWithFunctionCall(inst)) {
return false;
@@ -46,16 +47,22 @@
"The function should only be generated if something was modified.");
context()->AddFunction(std::move(opkill_function_));
}
+ if (opterminateinvocation_function_ != nullptr) {
+ assert(modified &&
+ "The function should only be generated if something was modified.");
+ context()->AddFunction(std::move(opterminateinvocation_function_));
+ }
return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
}
bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
- assert(inst->opcode() == SpvOpKill &&
- "|inst| must be an OpKill instruction.");
+ assert((inst->opcode() == SpvOpKill ||
+ inst->opcode() == SpvOpTerminateInvocation) &&
+ "|inst| must be an OpKill or OpTerminateInvocation instruction.");
InstructionBuilder ir_builder(
context(), inst,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- uint32_t func_id = GetOpKillFuncId();
+ uint32_t func_id = GetKillingFuncId(inst->opcode());
if (func_id == 0) {
return false;
}
@@ -108,13 +115,20 @@
return type_mgr->GetTypeInstruction(&func_type);
}
-uint32_t WrapOpKill::GetOpKillFuncId() {
- if (opkill_function_ != nullptr) {
- return opkill_function_->result_id();
+uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) {
+ // Parameterize by opcode
+ assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation);
+
+ std::unique_ptr<Function>* const killing_func =
+ (opcode == SpvOpKill) ? &opkill_function_
+ : &opterminateinvocation_function_;
+
+ if (*killing_func != nullptr) {
+ return (*killing_func)->result_id();
}
- uint32_t opkill_func_id = TakeNextId();
- if (opkill_func_id == 0) {
+ uint32_t killing_func_id = TakeNextId();
+ if (killing_func_id == 0) {
return 0;
}
@@ -125,15 +139,15 @@
// Generate the function start instruction
std::unique_ptr<Instruction> func_start(new Instruction(
- context(), SpvOpFunction, void_type_id, opkill_func_id, {}));
+ context(), SpvOpFunction, void_type_id, killing_func_id, {}));
func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
- opkill_function_.reset(new Function(std::move(func_start)));
+ (*killing_func).reset(new Function(std::move(func_start)));
// Generate the function end instruction
std::unique_ptr<Instruction> func_end(
new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
- opkill_function_->SetFunctionEnd(std::move(func_end));
+ (*killing_func)->SetFunctionEnd(std::move(func_end));
// Create the one basic block for the function.
uint32_t lab_id = TakeNextId();
@@ -146,21 +160,22 @@
// Add the OpKill to the basic block
std::unique_ptr<Instruction> kill_inst(
- new Instruction(context(), SpvOpKill, 0, 0, {}));
+ new Instruction(context(), opcode, 0, 0, {}));
bb->AddInstruction(std::move(kill_inst));
// Add the bb to the function
- bb->SetParent(opkill_function_.get());
- opkill_function_->AddBasicBlock(std::move(bb));
+ bb->SetParent((*killing_func).get());
+ (*killing_func)->AddBasicBlock(std::move(bb));
// Add the function to the module.
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
- opkill_function_->ForEachInst(
- [this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
+ (*killing_func)->ForEachInst([this](Instruction* inst) {
+ context()->AnalyzeDefUse(inst);
+ });
}
if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
- for (BasicBlock& basic_block : *opkill_function_) {
+ for (BasicBlock& basic_block : *(*killing_func)) {
context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
for (Instruction& inst : basic_block) {
context()->set_instr_block(&inst, &basic_block);
@@ -168,7 +183,7 @@
}
}
- return opkill_function_->result_id();
+ return (*killing_func)->result_id();
}
uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) {
diff --git a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
index 09f2dfa..7e43ca6 100644
--- a/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
+++ b/third_party/SPIRV-Tools/source/opt/wrap_opkill.h
@@ -38,10 +38,10 @@
}
private:
- // Replaces the OpKill instruction |inst| with a function call to a function
- // that contains a single instruction, which is OpKill. An OpUnreachable
- // instruction will be placed after the function call. Return true if
- // successful.
+ // Replaces the OpKill or OpTerminateInvocation instruction |inst| with a
+ // function call to a function that contains a single instruction, a clone of
+ // |inst|. An OpUnreachable instruction will be placed after the function
+ // call. Return true if successful.
bool ReplaceWithFunctionCall(Instruction* inst);
// Returns the id of the void type.
@@ -51,9 +51,9 @@
uint32_t GetVoidFunctionTypeId();
// Return the id of a function that has return type void, has no parameters,
- // and contains a single instruction, which is an OpKill. Returns 0 if the
- // function could not be generated.
- uint32_t GetOpKillFuncId();
+ // and contains a single instruction, which is |opcode|, either OpKill or
+ // OpTerminateInvocation. Returns 0 if the function could not be generated.
+ uint32_t GetKillingFuncId(SpvOp opcode);
// Returns the id of the return type for the function that contains |inst|.
// Returns 0 if |inst| is not in a function.
@@ -67,6 +67,11 @@
// function has a void return type and takes no parameters. If the function is
// |nullptr|, then the function has not been generated.
std::unique_ptr<Function> opkill_function_;
+ // The function that is a single instruction, which is an
+ // OpTerminateInvocation. The function has a void return type and takes no
+ // parameters. If the function is |nullptr|, then the function has not been
+ // generated.
+ std::unique_ptr<Function> opterminateinvocation_function_;
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
index b407f14..64fefbc 100644
--- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
+++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.cpp
@@ -22,6 +22,7 @@
spv_fuzzer_options_t::spv_fuzzer_options_t()
: has_random_seed(false),
random_seed(0),
+ replay_range(0),
replay_validation_enabled(false),
shrinker_step_limit(kDefaultStepLimit),
fuzzer_pass_validation_enabled(false) {}
@@ -45,6 +46,11 @@
options->random_seed = seed;
}
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+ spv_fuzzer_options options, int32_t replay_range) {
+ options->replay_range = replay_range;
+}
+
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
spv_fuzzer_options options, uint32_t shrinker_step_limit) {
options->shrinker_step_limit = shrinker_step_limit;
diff --git a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
index 143f77f..0db16a3 100644
--- a/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
+++ b/third_party/SPIRV-Tools/source/spirv_fuzzer_options.h
@@ -29,6 +29,9 @@
bool has_random_seed;
uint32_t random_seed;
+ // See spvFuzzerOptionsSetReplayRange.
+ int32_t replay_range;
+
// See spvFuzzerOptionsEnableReplayValidation.
bool replay_validation_enabled;
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.cpp b/third_party/SPIRV-Tools/source/val/basic_block.cpp
index a53103c..b2a8793 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.cpp
+++ b/third_party/SPIRV-Tools/source/val/basic_block.cpp
@@ -58,15 +58,9 @@
for (auto& block : next_blocks) {
block->predecessors_.push_back(this);
successors_.push_back(block);
- if (block->reachable_ == false) block->set_reachable(reachable_);
}
}
-void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) {
- if (branch_instruction == SpvOpUnreachable) reachable_ = false;
- return;
-}
-
bool BasicBlock::dominates(const BasicBlock& other) const {
return (this == &other) ||
!(other.dom_end() ==
diff --git a/third_party/SPIRV-Tools/source/val/basic_block.h b/third_party/SPIRV-Tools/source/val/basic_block.h
index 876105c..5eea4f9 100644
--- a/third_party/SPIRV-Tools/source/val/basic_block.h
+++ b/third_party/SPIRV-Tools/source/val/basic_block.h
@@ -106,9 +106,6 @@
/// Returns the immedate post dominator of this basic block
const BasicBlock* immediate_post_dominator() const;
- /// Ends the block without a successor
- void RegisterBranchInstruction(SpvOp branch_instruction);
-
/// Returns the label instruction for the block, or nullptr if not set.
const Instruction* label() const { return label_; }
diff --git a/third_party/SPIRV-Tools/source/val/function.cpp b/third_party/SPIRV-Tools/source/val/function.cpp
index 0281770..249c866 100644
--- a/third_party/SPIRV-Tools/source/val/function.cpp
+++ b/third_party/SPIRV-Tools/source/val/function.cpp
@@ -130,7 +130,6 @@
undefined_blocks_.erase(block_id);
current_block_ = &inserted_block->second;
ordered_blocks_.push_back(current_block_);
- if (IsFirstBlock(block_id)) current_block_->set_reachable(true);
} else if (success) { // Block doesn't exsist but this is not a definition
undefined_blocks_.insert(block_id);
}
@@ -138,8 +137,7 @@
return SPV_SUCCESS;
}
-void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
- SpvOp branch_instruction) {
+void Function::RegisterBlockEnd(std::vector<uint32_t> next_list) {
assert(
current_block_ &&
"RegisterBlockEnd can only be called when parsing a binary in a block");
@@ -174,7 +172,6 @@
}
}
- current_block_->RegisterBranchInstruction(branch_instruction);
current_block_->RegisterSuccessors(next_blocks);
current_block_ = nullptr;
return;
diff --git a/third_party/SPIRV-Tools/source/val/function.h b/third_party/SPIRV-Tools/source/val/function.h
index 0d6873d..400bb63 100644
--- a/third_party/SPIRV-Tools/source/val/function.h
+++ b/third_party/SPIRV-Tools/source/val/function.h
@@ -97,9 +97,7 @@
/// Registers the end of the block
///
/// @param[in] successors_list A list of ids to the block's successors
- /// @param[in] branch_instruction the branch instruction that ended the block
- void RegisterBlockEnd(std::vector<uint32_t> successors_list,
- SpvOp branch_instruction);
+ void RegisterBlockEnd(std::vector<uint32_t> successors_list);
/// Registers the end of the function. This is idempotent.
void RegisterFunctionEnd();
diff --git a/third_party/SPIRV-Tools/source/val/validate.cpp b/third_party/SPIRV-Tools/source/val/validate.cpp
index 168968d..f964b9b 100644
--- a/third_party/SPIRV-Tools/source/val/validate.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate.cpp
@@ -368,6 +368,10 @@
// Catch undefined forward references before performing further checks.
if (auto error = ValidateForwardDecls(*vstate)) return error;
+ // Calculate reachability after all the blocks are parsed, but early that it
+ // can be relied on in subsequent pases.
+ ReachabilityPass(*vstate);
+
// ID usage needs be handled in its own iteration of the instructions,
// between the two others. It depends on the first loop to have been
// finished, so that all instructions have been registered. And the following
diff --git a/third_party/SPIRV-Tools/source/val/validate.h b/third_party/SPIRV-Tools/source/val/validate.h
index 31a775b..3fc183d 100644
--- a/third_party/SPIRV-Tools/source/val/validate.h
+++ b/third_party/SPIRV-Tools/source/val/validate.h
@@ -197,6 +197,9 @@
/// Validates correctness of miscellaneous instructions.
spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
+/// Calculates the reachability of basic blocks.
+void ReachabilityPass(ValidationState_t& _);
+
/// Validates execution limitations.
///
/// Verifies execution models are allowed for all functionality they contain.
diff --git a/third_party/SPIRV-Tools/source/val/validate_atomics.cpp b/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
index b8867dd..df7973f 100644
--- a/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_atomics.cpp
@@ -54,11 +54,16 @@
spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
const SpvOp opcode = inst->opcode();
const uint32_t result_type = inst->type_id();
-
+ bool is_atomic_float_opcode = false;
+ if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore ||
+ opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) {
+ is_atomic_float_opcode = true;
+ }
switch (opcode) {
case SpvOpAtomicLoad:
case SpvOpAtomicStore:
case SpvOpAtomicExchange:
+ case SpvOpAtomicFAddEXT:
case SpvOpAtomicCompareExchange:
case SpvOpAtomicCompareExchangeWeak:
case SpvOpAtomicIIncrement:
@@ -92,11 +97,59 @@
} else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) {
assert(result_type == 0);
} else {
- if (!_.IsIntScalarType(result_type)) {
+ if (_.IsFloatScalarType(result_type)) {
+ if (is_atomic_float_opcode) {
+ if (opcode == SpvOpAtomicFAddEXT) {
+ if ((_.GetBitWidth(result_type) == 32) &&
+ (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": float add atomics require the AtomicFloat32AddEXT "
+ "capability";
+ }
+ if ((_.GetBitWidth(result_type) == 64) &&
+ (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": float add atomics require the AtomicFloat64AddEXT "
+ "capability";
+ }
+ }
+ } else {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": expected Result Type to be int scalar type";
+ }
+ } else if (_.IsIntScalarType(result_type) &&
+ opcode == SpvOpAtomicFAddEXT) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< spvOpcodeString(opcode)
- << ": expected Result Type to be int scalar type";
+ << ": expected Result Type to be float scalar type";
+ } else if (!_.IsFloatScalarType(result_type) &&
+ !_.IsIntScalarType(result_type)) {
+ switch (opcode) {
+ case SpvOpAtomicFAddEXT:
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": expected Result Type to be float scalar type";
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicSMin:
+ case SpvOpAtomicSMax:
+ case SpvOpAtomicUMin:
+ case SpvOpAtomicUMax:
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": expected Result Type to be integer scalar type";
+ default:
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << spvOpcodeString(opcode)
+ << ": expected Result Type to be int or float scalar type";
+ }
}
+
if (spvIsVulkanEnv(_.context()->target_env) &&
_.GetBitWidth(result_type) != 32) {
switch (opcode) {
@@ -108,11 +161,17 @@
case SpvOpAtomicOr:
case SpvOpAtomicXor:
case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicFAddEXT:
case SpvOpAtomicLoad:
case SpvOpAtomicStore:
case SpvOpAtomicExchange:
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicCompareExchangeWeak:
case SpvOpAtomicCompareExchange: {
if (_.GetBitWidth(result_type) == 64 &&
+ _.IsIntScalarType(result_type) &&
!_.HasCapability(SpvCapabilityInt64Atomics))
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< spvOpcodeString(opcode)
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index 1e33e51..8eb3a96 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -1062,7 +1062,7 @@
uint32_t target = inst->GetOperandAs<uint32_t>(0);
CFG_ASSERT(FirstBlockAssert, target);
- _.current_function().RegisterBlockEnd({target}, opcode);
+ _.current_function().RegisterBlockEnd({target});
} break;
case SpvOpBranchConditional: {
uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
@@ -1070,7 +1070,7 @@
CFG_ASSERT(FirstBlockAssert, tlabel);
CFG_ASSERT(FirstBlockAssert, flabel);
- _.current_function().RegisterBlockEnd({tlabel, flabel}, opcode);
+ _.current_function().RegisterBlockEnd({tlabel, flabel});
} break;
case SpvOpSwitch: {
@@ -1080,7 +1080,7 @@
CFG_ASSERT(FirstBlockAssert, target);
cases.push_back(target);
}
- _.current_function().RegisterBlockEnd({cases}, opcode);
+ _.current_function().RegisterBlockEnd({cases});
} break;
case SpvOpReturn: {
const uint32_t return_type = _.current_function().GetResultTypeId();
@@ -1090,18 +1090,24 @@
return _.diag(SPV_ERROR_INVALID_CFG, inst)
<< "OpReturn can only be called from a function with void "
<< "return type.";
- _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+ _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
break;
}
case SpvOpKill:
case SpvOpReturnValue:
case SpvOpUnreachable:
- _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+ case SpvOpTerminateInvocation:
+ _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
if (opcode == SpvOpKill) {
_.current_function().RegisterExecutionModelLimitation(
SpvExecutionModelFragment,
"OpKill requires Fragment execution model");
}
+ if (opcode == SpvOpTerminateInvocation) {
+ _.current_function().RegisterExecutionModelLimitation(
+ SpvExecutionModelFragment,
+ "OpTerminateInvocation requires Fragment execution model");
+ }
break;
default:
break;
@@ -1109,6 +1115,27 @@
return SPV_SUCCESS;
}
+void ReachabilityPass(ValidationState_t& _) {
+ for (auto& f : _.functions()) {
+ std::vector<BasicBlock*> stack;
+ auto entry = f.first_block();
+ // Skip function declarations.
+ if (entry) stack.push_back(entry);
+
+ while (!stack.empty()) {
+ auto block = stack.back();
+ stack.pop_back();
+
+ if (block->reachable()) continue;
+
+ block->set_reachable(true);
+ for (auto succ : *block->successors()) {
+ stack.push_back(succ);
+ }
+ }
+ }
+}
+
spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
switch (inst->opcode()) {
case SpvOpPhi:
diff --git a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
index ce09e18..d381276 100644
--- a/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_decorations.cpp
@@ -1540,6 +1540,21 @@
return SPV_SUCCESS;
}
+spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
+ const Instruction& inst,
+ const Decoration& decoration) {
+ if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS;
+
+ if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+ inst.opcode() == SpvOpTypeStruct) {
+ return SPV_SUCCESS;
+ }
+
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << "Location decoration can only be applied to a variable or member "
+ "of a structure type";
+}
+
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
{ \
spv_result_t e##LINE = (X); \
@@ -1590,6 +1605,9 @@
case SpvDecorationBufferBlock:
PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
break;
+ case SpvDecorationLocation:
+ PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
+ break;
default:
break;
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
index 6478b3c..9d395fb 100644
--- a/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_instruction.cpp
@@ -296,7 +296,12 @@
<< SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier";
}
- if (inst_desc->numCapabilities > 0u) {
+ // OpTerminateInvocation is special because it is enabled by Shader
+ // capability, but also requries a extension and/or version check.
+ const bool capability_check_is_sufficient =
+ inst->opcode() != SpvOpTerminateInvocation;
+
+ if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) {
// We already checked that the direct capability dependency has been
// satisfied. We don't need to check any further.
return SPV_SUCCESS;
diff --git a/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp b/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
index c85b673..833734f 100644
--- a/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_interfaces.cpp
@@ -102,6 +102,365 @@
return SPV_SUCCESS;
}
+// This function assumes a base location has been determined already. As such
+// any further location decorations are invalid.
+// TODO: if this code turns out to be slow, there is an opportunity to cache
+// the result for a given type id.
+spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
+ uint32_t* num_locations) {
+ *num_locations = 0;
+ switch (type->opcode()) {
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ // Scalars always consume a single location.
+ *num_locations = 1;
+ break;
+ case SpvOpTypeVector:
+ // 3- and 4-component 64-bit vectors consume two locations.
+ if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) ||
+ _.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) &&
+ (type->GetOperandAs<uint32_t>(2) > 2)) {
+ *num_locations = 2;
+ } else {
+ *num_locations = 1;
+ }
+ break;
+ case SpvOpTypeMatrix:
+ // Matrices consume locations equal to the underlying vector type for
+ // each column.
+ NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+ num_locations);
+ *num_locations *= type->GetOperandAs<uint32_t>(2);
+ break;
+ case SpvOpTypeArray: {
+ // Arrays consume locations equal to the underlying type times the number
+ // of elements in the vector.
+ NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+ num_locations);
+ bool is_int = false;
+ bool is_const = false;
+ uint32_t value = 0;
+ // Attempt to evaluate the number of array elements.
+ std::tie(is_int, is_const, value) =
+ _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+ if (is_int && is_const) *num_locations *= value;
+ break;
+ }
+ case SpvOpTypeStruct: {
+ // Members cannot have location decorations at this point.
+ if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, type)
+ << "Members cannot be assigned a location";
+ }
+
+ // Structs consume locations equal to the sum of the locations consumed
+ // by the members.
+ for (uint32_t i = 1; i < type->operands().size(); ++i) {
+ uint32_t member_locations = 0;
+ if (auto error = NumConsumedLocations(
+ _, _.FindDef(type->GetOperandAs<uint32_t>(i)),
+ &member_locations)) {
+ return error;
+ }
+ *num_locations += member_locations;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return SPV_SUCCESS;
+}
+
+// Returns the number of components consumed by types that support a component
+// decoration.
+uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
+ uint32_t num_components = 0;
+ switch (type->opcode()) {
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ // 64-bit types consume two components.
+ if (type->GetOperandAs<uint32_t>(1) == 64) {
+ num_components = 2;
+ } else {
+ num_components = 1;
+ }
+ break;
+ case SpvOpTypeVector:
+ // Vectors consume components equal to the underlying type's consumption
+ // times the number of elements in the vector. Note that 3- and 4-element
+ // vectors cannot have a component decoration (i.e. assumed to be zero).
+ num_components =
+ NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
+ num_components *= type->GetOperandAs<uint32_t>(2);
+ break;
+ default:
+ // This is an error that is validated elsewhere.
+ break;
+ }
+
+ return num_components;
+}
+
+// Populates |locations| (and/or |output_index1_locations|) with the use
+// location and component coordinates for |variable|. Indices are calculated as
+// 4 * location + component.
+spv_result_t GetLocationsForVariable(
+ ValidationState_t& _, const Instruction* entry_point,
+ const Instruction* variable, std::unordered_set<uint32_t>* locations,
+ std::unordered_set<uint32_t>* output_index1_locations) {
+ const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
+ SpvExecutionModelFragment;
+ const bool is_output =
+ variable->GetOperandAs<SpvStorageClass>(2) == SpvStorageClassOutput;
+ auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
+ auto ptr_type = _.FindDef(ptr_type_id);
+ auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
+ auto type = _.FindDef(type_id);
+
+ // Check for Location, Component and Index decorations on the variable. The
+ // validator allows duplicate decorations if the location/component/index are
+ // equal. Also track Patch and PerTaskNV decorations.
+ bool has_location = false;
+ uint32_t location = 0;
+ bool has_component = false;
+ uint32_t component = 0;
+ bool has_index = false;
+ uint32_t index = 0;
+ bool has_patch = false;
+ bool has_per_task_nv = false;
+ bool has_per_vertex_nv = false;
+ for (auto& dec : _.id_decorations(variable->id())) {
+ if (dec.dec_type() == SpvDecorationLocation) {
+ if (has_location && dec.params()[0] != location) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << "Variable has conflicting location decorations";
+ }
+ has_location = true;
+ location = dec.params()[0];
+ } else if (dec.dec_type() == SpvDecorationComponent) {
+ if (has_component && dec.params()[0] != component) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << "Variable has conflicting component decorations";
+ }
+ has_component = true;
+ component = dec.params()[0];
+ } else if (dec.dec_type() == SpvDecorationIndex) {
+ if (!is_output || !is_fragment) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << "Index can only be applied to Fragment output variables";
+ }
+ if (has_index && dec.params()[0] != index) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << "Variable has conflicting index decorations";
+ }
+ has_index = true;
+ index = dec.params()[0];
+ } else if (dec.dec_type() == SpvDecorationBuiltIn) {
+ // Don't check built-ins.
+ return SPV_SUCCESS;
+ } else if (dec.dec_type() == SpvDecorationPatch) {
+ has_patch = true;
+ } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
+ has_per_task_nv = true;
+ } else if (dec.dec_type() == SpvDecorationPerVertexNV) {
+ has_per_vertex_nv = true;
+ }
+ }
+
+ // Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and
+ // tessellation control, evaluation and geometry per-vertex inputs have a
+ // layer of arraying that is not included in interface matching.
+ bool is_arrayed = false;
+ switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
+ case SpvExecutionModelTessellationControl:
+ if (!has_patch) {
+ is_arrayed = true;
+ }
+ break;
+ case SpvExecutionModelTessellationEvaluation:
+ if (!is_output && !has_patch) {
+ is_arrayed = true;
+ }
+ break;
+ case SpvExecutionModelGeometry:
+ if (!is_output) {
+ is_arrayed = true;
+ }
+ break;
+ case SpvExecutionModelFragment:
+ if (!is_output && has_per_vertex_nv) {
+ is_arrayed = true;
+ }
+ break;
+ case SpvExecutionModelMeshNV:
+ if (is_output && !has_per_task_nv) {
+ is_arrayed = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Unpack arrayness.
+ if (is_arrayed && (type->opcode() == SpvOpTypeArray ||
+ type->opcode() == SpvOpTypeRuntimeArray)) {
+ type_id = type->GetOperandAs<uint32_t>(1);
+ type = _.FindDef(type_id);
+ }
+
+ if (type->opcode() == SpvOpTypeStruct) {
+ // Don't check built-ins.
+ if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS;
+ }
+
+ // Only block-decorated structs don't need a location on the variable.
+ const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
+ if (!has_location && !is_block) {
+ return _.diag(SPV_ERROR_INVALID_DATA, variable)
+ << "Variable must be decorated with a location";
+ }
+
+ const std::string storage_class = is_output ? "output" : "input";
+ if (has_location) {
+ auto sub_type = type;
+ bool is_int = false;
+ bool is_const = false;
+ uint32_t array_size = 1;
+ // If the variable is still arrayed, mark the locations/components per
+ // index.
+ if (type->opcode() == SpvOpTypeArray) {
+ // Determine the array size if possible and get the element type.
+ std::tie(is_int, is_const, array_size) =
+ _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+ if (!is_int || !is_const) array_size = 1;
+ auto sub_type_id = type->GetOperandAs<uint32_t>(1);
+ sub_type = _.FindDef(sub_type_id);
+ }
+
+ for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
+ uint32_t num_locations = 0;
+ if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
+ return error;
+
+ uint32_t num_components = NumConsumedComponents(_, sub_type);
+ uint32_t array_location = location + (num_locations * array_idx);
+ uint32_t start = array_location * 4;
+ uint32_t end = (array_location + num_locations) * 4;
+ if (num_components != 0) {
+ start += component;
+ end = array_location * 4 + component + num_components;
+ }
+
+ auto locs = locations;
+ if (has_index && index == 1) locs = output_index1_locations;
+
+ for (uint32_t i = start; i < end; ++i) {
+ if (!locs->insert(i).second) {
+ return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+ << "Entry-point has conflicting " << storage_class
+ << " location assignment at location " << i / 4
+ << ", component " << i % 4;
+ }
+ }
+ }
+ } else {
+ // For Block-decorated structs with no location assigned to the variable,
+ // each member of the block must be assigned a location. Also record any
+ // member component assignments. The validator allows duplicate decorations
+ // if they agree on the location/component.
+ std::unordered_map<uint32_t, uint32_t> member_locations;
+ std::unordered_map<uint32_t, uint32_t> member_components;
+ for (auto& dec : _.id_decorations(type_id)) {
+ if (dec.dec_type() == SpvDecorationLocation) {
+ auto where = member_locations.find(dec.struct_member_index());
+ if (where == member_locations.end()) {
+ member_locations[dec.struct_member_index()] = dec.params()[0];
+ } else if (where->second != dec.params()[0]) {
+ return _.diag(SPV_ERROR_INVALID_DATA, type)
+ << "Member index " << dec.struct_member_index()
+ << " has conflicting location assignments";
+ }
+ } else if (dec.dec_type() == SpvDecorationComponent) {
+ auto where = member_components.find(dec.struct_member_index());
+ if (where == member_components.end()) {
+ member_components[dec.struct_member_index()] = dec.params()[0];
+ } else if (where->second != dec.params()[0]) {
+ return _.diag(SPV_ERROR_INVALID_DATA, type)
+ << "Member index " << dec.struct_member_index()
+ << " has conflicting component assignments";
+ }
+ }
+ }
+
+ for (uint32_t i = 1; i < type->operands().size(); ++i) {
+ auto where = member_locations.find(i - 1);
+ if (where == member_locations.end()) {
+ return _.diag(SPV_ERROR_INVALID_DATA, type)
+ << "Member index " << i - 1
+ << " is missing a location assignment";
+ }
+
+ location = where->second;
+ auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
+ uint32_t num_locations = 0;
+ if (auto error = NumConsumedLocations(_, member, &num_locations))
+ return error;
+
+ // If the component is not specified, it is assumed to be zero.
+ uint32_t num_components = NumConsumedComponents(_, member);
+ component = 0;
+ if (member_components.count(i - 1)) {
+ component = member_components[i - 1];
+ }
+
+ uint32_t start = location * 4;
+ uint32_t end = (location + num_locations) * 4;
+ if (num_components != 0) {
+ start += component;
+ end = location * 4 + component + num_components;
+ }
+ for (uint32_t l = start; l < end; ++l) {
+ if (!locations->insert(l).second) {
+ return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+ << "Entry-point has conflicting " << storage_class
+ << " location assignment at location " << l / 4
+ << ", component " << l % 4;
+ }
+ }
+ }
+ }
+
+ return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLocations(ValidationState_t& _,
+ const Instruction* entry_point) {
+ // Locations are stored as a combined location and component values.
+ std::unordered_set<uint32_t> input_locations;
+ std::unordered_set<uint32_t> output_locations_index0;
+ std::unordered_set<uint32_t> output_locations_index1;
+ for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
+ auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
+ auto interface_var = _.FindDef(interface_id);
+ auto storage_class = interface_var->GetOperandAs<SpvStorageClass>(2);
+ if (storage_class != SpvStorageClassInput &&
+ storage_class != SpvStorageClassOutput) {
+ continue;
+ }
+
+ auto locations = (storage_class == SpvStorageClassInput)
+ ? &input_locations
+ : &output_locations_index0;
+ if (auto error = GetLocationsForVariable(
+ _, entry_point, interface_var, locations, &output_locations_index1))
+ return error;
+ }
+
+ return SPV_SUCCESS;
+}
+
} // namespace
spv_result_t ValidateInterfaces(ValidationState_t& _) {
@@ -114,6 +473,17 @@
}
}
+ if (spvIsVulkanEnv(_.context()->target_env)) {
+ for (auto& inst : _.ordered_instructions()) {
+ if (inst.opcode() == SpvOpEntryPoint) {
+ if (auto error = ValidateLocations(_, &inst)) {
+ return error;
+ }
+ }
+ if (inst.opcode() == SpvOpTypeVoid) break;
+ }
+ }
+
return SPV_SUCCESS;
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
index e020f5a..a7f8d33 100644
--- a/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_mode_setting.cpp
@@ -501,10 +501,6 @@
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Addressing model must be Logical for WebGPU environment.";
}
- if (_.memory_model() != SpvMemoryModelVulkanKHR) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Memory model must be VulkanKHR for WebGPU environment.";
- }
}
if (spvIsOpenCLEnv(_.context()->target_env)) {
diff --git a/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp b/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
index a0379c1..184ae4f 100644
--- a/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
+++ b/third_party/SPIRV-Tools/test/enum_string_mapping_test.cpp
@@ -177,6 +177,8 @@
{SpvCapabilityStoragePushConstant16, "StoragePushConstant16"},
{SpvCapabilityStorageInputOutput16, "StorageInputOutput16"},
{SpvCapabilityDeviceGroup, "DeviceGroup"},
+ {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"},
+ {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"},
{SpvCapabilityMultiView, "MultiView"},
{SpvCapabilitySampleMaskOverrideCoverageNV,
"SampleMaskOverrideCoverageNV"},
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index dca142a..7047749 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -24,19 +24,25 @@
fuzzer_pass_construct_composites_test.cpp
fuzzer_pass_donate_modules_test.cpp
instruction_descriptor_test.cpp
+ replayer_test.cpp
transformation_access_chain_test.cpp
transformation_add_constant_boolean_test.cpp
transformation_add_constant_composite_test.cpp
transformation_add_constant_null_test.cpp
transformation_add_constant_scalar_test.cpp
+ transformation_add_copy_memory_test.cpp
transformation_add_dead_block_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_dead_continue_test.cpp
transformation_add_function_test.cpp
transformation_add_global_undef_test.cpp
transformation_add_global_variable_test.cpp
+ transformation_add_image_sample_unused_components_test.cpp
transformation_add_local_variable_test.cpp
transformation_add_no_contraction_decoration_test.cpp
+ transformation_add_parameter_test.cpp
+ transformation_add_relaxed_decoration_test.cpp
+ transformation_add_synonym_test.cpp
transformation_add_type_array_test.cpp
transformation_add_type_boolean_test.cpp
transformation_add_type_float_test.cpp
@@ -50,17 +56,25 @@
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_compute_data_synonym_fact_closure_test.cpp
- transformation_copy_object_test.cpp
transformation_equation_instruction_test.cpp
transformation_function_call_test.cpp
+ transformation_invert_comparison_operator_test.cpp
transformation_load_test.cpp
transformation_merge_blocks_test.cpp
transformation_move_block_down_test.cpp
transformation_outline_function_test.cpp
transformation_permute_function_parameters_test.cpp
+ transformation_permute_phi_operands_test.cpp
+ transformation_push_id_through_variable_test.cpp
+ transformation_replace_parameter_with_global_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
+ transformation_replace_copy_object_with_store_load_test.cpp
transformation_replace_constant_with_uniform_test.cpp
+ transformation_replace_copy_memory_with_load_store_test.cpp
transformation_replace_id_with_synonym_test.cpp
+ transformation_replace_linear_algebra_instruction_test.cpp
+ transformation_replace_load_store_with_copy_memory_test.cpp
+ transformation_replace_params_with_struct_test.cpp
transformation_set_function_control_test.cpp
transformation_set_loop_control_test.cpp
transformation_set_memory_operands_mask_test.cpp
@@ -68,7 +82,9 @@
transformation_split_block_test.cpp
transformation_store_test.cpp
transformation_swap_commutable_operands_test.cpp
+ transformation_swap_conditional_branch_operands_test.cpp
transformation_toggle_access_chain_instruction_test.cpp
+ transformation_record_synonymous_constants_test.cpp
transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
diff --git a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
index 8b1e0c4..bce10b9 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
@@ -782,6 +782,94 @@
MakeDataDescriptor(11, {2, 3})));
}
+TEST(FactManagerTest, CorollaryConversionFacts) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeVector %6 2
+ %9 = OpTypeVector %7 2
+ %10 = OpTypeFloat 32
+ %11 = OpTypeVector %10 2
+ %15 = OpConstant %6 24 ; synonym of %16
+ %16 = OpConstant %6 24
+ %17 = OpConstant %7 24 ; synonym of %18
+ %18 = OpConstant %7 24
+ %19 = OpConstantComposite %8 %15 %15 ; synonym of %20
+ %20 = OpConstantComposite %8 %16 %16
+ %21 = OpConstantComposite %9 %17 %17 ; synonym of %22
+ %22 = OpConstantComposite %9 %18 %18
+ %23 = OpConstantComposite %8 %15 %15 ; not a synonym of %19
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %24 = OpConvertSToF %10 %15 ; synonym of %25
+ %25 = OpConvertSToF %10 %16
+ %26 = OpConvertUToF %10 %17 ; not a synonym of %27 (different opcode)
+ %27 = OpConvertSToF %10 %18
+ %28 = OpConvertUToF %11 %19 ; synonym of %29
+ %29 = OpConvertUToF %11 %20
+ %30 = OpConvertSToF %11 %21 ; not a synonym of %31 (different opcode)
+ %31 = OpConvertUToF %11 %22
+ %32 = OpConvertUToF %11 %23 ; not a synonym of %28 (operand is not synonymous)
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Add equation facts
+ fact_manager.AddFactIdEquation(24, SpvOpConvertSToF, {15}, context.get());
+ fact_manager.AddFactIdEquation(25, SpvOpConvertSToF, {16}, context.get());
+ fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}, context.get());
+ fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {18}, context.get());
+ fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {19}, context.get());
+ fact_manager.AddFactIdEquation(29, SpvOpConvertUToF, {20}, context.get());
+ fact_manager.AddFactIdEquation(30, SpvOpConvertSToF, {21}, context.get());
+ fact_manager.AddFactIdEquation(31, SpvOpConvertUToF, {22}, context.get());
+ fact_manager.AddFactIdEquation(32, SpvOpConvertUToF, {23}, context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}),
+ MakeDataDescriptor(16, {}), context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}),
+ MakeDataDescriptor(25, {})));
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}),
+ MakeDataDescriptor(18, {}), context.get());
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}),
+ MakeDataDescriptor(27, {})));
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(19, {}),
+ MakeDataDescriptor(20, {}), context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+ MakeDataDescriptor(29, {})));
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(22, {}), context.get());
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}),
+ MakeDataDescriptor(31, {})));
+
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+ MakeDataDescriptor(28, {})));
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}),
+ MakeDataDescriptor(19, {}), context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+ MakeDataDescriptor(28, {})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+ MakeDataDescriptor(29, {})));
+}
+
TEST(FactManagerTest, LogicalNotEquationFacts) {
std::string shader = R"(
OpCapability Shader
@@ -982,6 +1070,91 @@
MakeDataDescriptor(16, {})));
}
+TEST(FactManagerTest, ConversionEquations) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %5 = OpTypeInt 32 0
+ %6 = OpTypeFloat 32
+ %14 = OpTypeVector %4 2
+ %15 = OpTypeVector %5 2
+ %24 = OpTypeVector %6 2
+ %16 = OpConstant %4 32 ; synonym of %17
+ %17 = OpConstant %4 32
+ %18 = OpConstant %5 32 ; synonym of %19
+ %19 = OpConstant %5 32
+ %20 = OpConstantComposite %14 %16 %16 ; synonym of %21
+ %21 = OpConstantComposite %14 %17 %17
+ %22 = OpConstantComposite %15 %18 %18 ; synonym of %23
+ %23 = OpConstantComposite %15 %19 %19
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %25 = OpConvertUToF %6 %16 ; synonym of %26
+ %26 = OpConvertUToF %6 %17
+ %27 = OpConvertSToF %24 %20 ; not a synonym of %28 (wrong opcode)
+ %28 = OpConvertUToF %24 %21
+ %29 = OpConvertSToF %6 %18 ; not a synonym of %30 (wrong opcode)
+ %30 = OpConvertUToF %6 %19
+ %31 = OpConvertSToF %24 %22 ; synonym of %32
+ %32 = OpConvertSToF %24 %23
+ %33 = OpConvertUToF %6 %17 ; synonym of %26
+ %34 = OpConvertSToF %24 %23 ; synonym of %32
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}),
+ MakeDataDescriptor(17, {}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}),
+ MakeDataDescriptor(19, {}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(20, {}),
+ MakeDataDescriptor(21, {}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}),
+ MakeDataDescriptor(23, {}), context.get());
+
+ fact_manager.AddFactIdEquation(25, SpvOpConvertUToF, {16}, context.get());
+ fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(26, {})));
+
+ fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {20}, context.get());
+ fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {21}, context.get());
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}),
+ MakeDataDescriptor(28, {})));
+
+ fact_manager.AddFactIdEquation(29, SpvOpConvertSToF, {18}, context.get());
+ fact_manager.AddFactIdEquation(30, SpvOpConvertUToF, {19}, context.get());
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(29, {}),
+ MakeDataDescriptor(30, {})));
+
+ fact_manager.AddFactIdEquation(31, SpvOpConvertSToF, {22}, context.get());
+ fact_manager.AddFactIdEquation(32, SpvOpConvertSToF, {23}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(31, {}),
+ MakeDataDescriptor(32, {})));
+
+ fact_manager.AddFactIdEquation(33, SpvOpConvertUToF, {17}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}),
+ MakeDataDescriptor(26, {})));
+
+ fact_manager.AddFactIdEquation(34, SpvOpConvertSToF, {23}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}),
+ MakeDataDescriptor(34, {})));
+}
+
TEST(FactManagerTest, EquationAndEquivalenceFacts) {
std::string shader = R"(
OpCapability Shader
@@ -1110,6 +1283,41 @@
ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one));
}
+TEST(FactManagerTest, IdIsIrrelevant) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %12 = OpConstant %6 0
+ %13 = OpConstant %6 1
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(12));
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+
+ fact_manager.AddFactIdIsIrrelevant(12);
+
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(12));
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(13));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index 0833c1d..0be3d5a 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <algorithm>
+
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -1664,7 +1666,8 @@
TransformationContext transformation_context(&fact_manager,
validator_options);
- FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
+ PseudoRandomGenerator rng(0);
+ FuzzerContext fuzzer_context(&rng, 100);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
@@ -1678,6 +1681,227 @@
ASSERT_TRUE(IsValid(env, recipient_context.get()));
}
+TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) {
+ std::string donor_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeStruct %6 %6 %7
+ %9 = OpSpecConstantTrue %6
+ %10 = OpSpecConstantFalse %6
+ %11 = OpSpecConstant %7 2
+ %12 = OpSpecConstantComposite %8 %9 %10 %11
+ %13 = OpSpecConstantOp %6 LogicalEqual %9 %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ std::string recipient_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto recipient_context =
+ BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+ const auto donor_context =
+ BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ PseudoRandomGenerator prng(0);
+ FuzzerContext fuzzer_context(&prng, 100);
+ protobufs::TransformationSequence transformation_sequence;
+
+ FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+ &transformation_context, &fuzzer_context,
+ &transformation_sequence, {});
+
+ fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+ // Check that the module is valid first.
+ ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %100 = OpTypeBool
+ %101 = OpTypeInt 32 1
+ %102 = OpTypeStruct %100 %100 %101
+ %103 = OpConstantTrue %100
+ %104 = OpConstantFalse %100
+ %105 = OpConstant %101 2
+ %106 = OpConstantComposite %102 %103 %104 %105
+ %107 = OpSpecConstantOp %100 LogicalEqual %103 %104
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %108 = OpFunction %2 None %3
+ %109 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ // Now check that the transformation has produced the expected result.
+ ASSERT_TRUE(IsEqual(env, expected_shader, recipient_context.get()));
+}
+
+TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) {
+ std::string donor_shader = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_storage_buffer_storage_class"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %29 "kernel_1"
+ OpEntryPoint GLCompute %37 "kernel_2"
+ OpSource OpenCL_C 120
+ OpDecorate %2 ArrayStride 4
+ OpMemberDecorate %3 0 Offset 0
+ OpDecorate %3 Block
+ OpMemberDecorate %5 0 Offset 0
+ OpMemberDecorate %6 0 Offset 0
+ OpDecorate %6 Block
+ OpDecorate %21 BuiltIn WorkgroupSize
+ OpDecorate %23 DescriptorSet 0
+ OpDecorate %23 Binding 0
+ OpDecorate %25 SpecId 3
+ OpDecorate %18 SpecId 0
+ OpDecorate %19 SpecId 1
+ OpDecorate %20 SpecId 2
+ %1 = OpTypeInt 32 0
+ %2 = OpTypeRuntimeArray %1
+ %3 = OpTypeStruct %2
+ %4 = OpTypePointer StorageBuffer %3
+ %5 = OpTypeStruct %1
+ %6 = OpTypeStruct %5
+ %7 = OpTypePointer PushConstant %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVoid
+ %10 = OpTypeFunction %9
+ %11 = OpTypePointer Workgroup %1
+ %12 = OpTypePointer PushConstant %5
+ %13 = OpTypePointer StorageBuffer %1
+ %14 = OpTypeFunction %1 %1
+ %15 = OpTypeVector %1 3
+ %16 = OpTypePointer Private %15
+ %17 = OpConstant %1 0
+ %18 = OpSpecConstant %1 1
+ %19 = OpSpecConstant %1 1
+ %20 = OpSpecConstant %1 1
+ %21 = OpSpecConstantComposite %15 %18 %19 %20
+ %25 = OpSpecConstant %1 1
+ %26 = OpTypeArray %1 %25
+ %27 = OpTypePointer Workgroup %26
+ %22 = OpVariable %16 Private %21
+ %23 = OpVariable %4 StorageBuffer
+ %24 = OpVariable %7 PushConstant
+ %28 = OpVariable %27 Workgroup
+ %29 = OpFunction %9 None %10
+ %30 = OpLabel
+ %31 = OpAccessChain %11 %28 %17
+ %32 = OpAccessChain %12 %24 %17
+ %33 = OpLoad %5 %32
+ %34 = OpCompositeExtract %1 %33 0
+ %35 = OpFunctionCall %1 %45 %34
+ %36 = OpAccessChain %13 %23 %17 %34
+ OpStore %36 %35
+ OpReturn
+ OpFunctionEnd
+ %37 = OpFunction %9 None %10
+ %38 = OpLabel
+ %39 = OpAccessChain %11 %28 %17
+ %40 = OpAccessChain %12 %24 %17
+ %41 = OpLoad %5 %40
+ %42 = OpCompositeExtract %1 %41 0
+ %43 = OpFunctionCall %1 %45 %42
+ %44 = OpAccessChain %13 %23 %17 %42
+ OpStore %44 %43
+ OpReturn
+ OpFunctionEnd
+ %45 = OpFunction %1 Pure %14
+ %46 = OpFunctionParameter %1
+ %47 = OpLabel
+ %48 = OpAccessChain %11 %28 %46
+ %49 = OpLoad %1 %48
+ OpReturnValue %49
+ OpFunctionEnd
+ )";
+
+ std::string recipient_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_0;
+ const auto consumer = nullptr;
+ const auto recipient_context =
+ BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, recipient_context.get()));
+
+ const auto donor_context =
+ BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, donor_context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ PseudoRandomGenerator rng(0);
+ FuzzerContext fuzzer_context(&rng, 100);
+ protobufs::TransformationSequence transformation_sequence;
+
+ FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
+ &transformation_context, &fuzzer_context,
+ &transformation_sequence, {});
+
+ fuzzer_pass.DonateSingleModule(donor_context.get(), false);
+
+ ASSERT_TRUE(IsValid(env, recipient_context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
index 1e7c643..8a23574 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -1659,6 +1659,8 @@
replayer.SetMessageConsumer(kConsoleMessageConsumer);
auto replayer_result_status = replayer.Run(
binary_in, initial_facts, fuzzer_transformation_sequence_out,
+ static_cast<uint32_t>(
+ fuzzer_transformation_sequence_out.transformation_size()),
&replayer_binary_out, &replayer_transformation_sequence_out);
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
replayer_result_status);
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
index 24b4460..709e9ce 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_shrinker_test.cpp
@@ -1111,6 +1111,18 @@
*temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
*facts.mutable_fact()->Add() = temp;
}
+ // Also add an invalid fact, which should be ignored.
+ {
+ protobufs::FactConstantUniform bad_fact;
+ // The descriptor set, binding and indices used here deliberately make no
+ // sense.
+ *bad_fact.mutable_uniform_buffer_element_descriptor() =
+ MakeUniformBufferElementDescriptor(22, 33, {44, 55});
+ *bad_fact.mutable_constant_word()->Add() = 100;
+ protobufs::Fact temp;
+ *temp.mutable_constant_uniform_fact() = bad_fact;
+ *facts.mutable_fact()->Add() = temp;
+ }
// Do 2 fuzzer runs, starting from an initial seed of 194 (seed value chosen
// arbitrarily).
diff --git a/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp
new file mode 100644
index 0000000..5d6169e
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/replayer_test.cpp
@@ -0,0 +1,301 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/replayer.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_split_block.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(ReplayerTest, PartialReplay) {
+ const std::string kTestShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "g"
+ OpName %11 "x"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Private %6
+ %8 = OpVariable %7 Private
+ %9 = OpConstant %6 10
+ %10 = OpTypePointer Function %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ OpStore %8 %9
+ %12 = OpLoad %6 %8
+ OpStore %11 %12
+ %13 = OpLoad %6 %8
+ OpStore %11 %13
+ %14 = OpLoad %6 %8
+ OpStore %11 %14
+ %15 = OpLoad %6 %8
+ OpStore %11 %15
+ %16 = OpLoad %6 %8
+ OpStore %11 %16
+ %17 = OpLoad %6 %8
+ OpStore %11 %17
+ %18 = OpLoad %6 %8
+ OpStore %11 %18
+ %19 = OpLoad %6 %8
+ OpStore %11 %19
+ %20 = OpLoad %6 %8
+ OpStore %11 %20
+ %21 = OpLoad %6 %8
+ OpStore %11 %21
+ %22 = OpLoad %6 %8
+ OpStore %11 %22
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ spvtools::ValidatorOptions validator_options;
+
+ std::vector<uint32_t> binary_in;
+ SpirvTools t(env);
+ t.SetMessageConsumer(kSilentConsumer);
+ ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
+ ASSERT_TRUE(t.Validate(binary_in));
+
+ protobufs::TransformationSequence transformations;
+ for (uint32_t id = 12; id <= 22; id++) {
+ *transformations.add_transformation() =
+ TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0),
+ id + 100)
+ .ToMessage();
+ }
+
+ {
+ // Full replay
+ protobufs::TransformationSequence transformations_out;
+ protobufs::FactSequence empty_facts;
+ std::vector<uint32_t> binary_out;
+ Replayer replayer(env, true, validator_options);
+ replayer.SetMessageConsumer(kSilentConsumer);
+ auto replayer_result_status =
+ replayer.Run(binary_in, empty_facts, transformations, 11, &binary_out,
+ &transformations_out);
+ // Replay should succeed.
+ ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+ replayer_result_status);
+ // All transformations should be applied.
+ ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
+ transformations, transformations_out));
+
+ const std::string kFullySplitShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "g"
+ OpName %11 "x"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Private %6
+ %8 = OpVariable %7 Private
+ %9 = OpConstant %6 10
+ %10 = OpTypePointer Function %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ OpStore %8 %9
+ OpBranch %112
+ %112 = OpLabel
+ %12 = OpLoad %6 %8
+ OpStore %11 %12
+ OpBranch %113
+ %113 = OpLabel
+ %13 = OpLoad %6 %8
+ OpStore %11 %13
+ OpBranch %114
+ %114 = OpLabel
+ %14 = OpLoad %6 %8
+ OpStore %11 %14
+ OpBranch %115
+ %115 = OpLabel
+ %15 = OpLoad %6 %8
+ OpStore %11 %15
+ OpBranch %116
+ %116 = OpLabel
+ %16 = OpLoad %6 %8
+ OpStore %11 %16
+ OpBranch %117
+ %117 = OpLabel
+ %17 = OpLoad %6 %8
+ OpStore %11 %17
+ OpBranch %118
+ %118 = OpLabel
+ %18 = OpLoad %6 %8
+ OpStore %11 %18
+ OpBranch %119
+ %119 = OpLabel
+ %19 = OpLoad %6 %8
+ OpStore %11 %19
+ OpBranch %120
+ %120 = OpLabel
+ %20 = OpLoad %6 %8
+ OpStore %11 %20
+ OpBranch %121
+ %121 = OpLabel
+ %21 = OpLoad %6 %8
+ OpStore %11 %21
+ OpBranch %122
+ %122 = OpLabel
+ %22 = OpLoad %6 %8
+ OpStore %11 %22
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, kFullySplitShader, binary_out));
+ }
+
+ {
+ // Half replay
+ protobufs::TransformationSequence transformations_out;
+ protobufs::FactSequence empty_facts;
+ std::vector<uint32_t> binary_out;
+ Replayer replayer(env, true, validator_options);
+ replayer.SetMessageConsumer(kSilentConsumer);
+ auto replayer_result_status =
+ replayer.Run(binary_in, empty_facts, transformations, 5, &binary_out,
+ &transformations_out);
+ // Replay should succeed.
+ ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+ replayer_result_status);
+ // The first 5 transformations should be applied
+ ASSERT_EQ(5, transformations_out.transformation_size());
+ for (uint32_t i = 0; i < 5; i++) {
+ ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
+ transformations.transformation(i),
+ transformations_out.transformation(i)));
+ }
+
+ const std::string kHalfSplitShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "g"
+ OpName %11 "x"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Private %6
+ %8 = OpVariable %7 Private
+ %9 = OpConstant %6 10
+ %10 = OpTypePointer Function %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ OpStore %8 %9
+ OpBranch %112
+ %112 = OpLabel
+ %12 = OpLoad %6 %8
+ OpStore %11 %12
+ OpBranch %113
+ %113 = OpLabel
+ %13 = OpLoad %6 %8
+ OpStore %11 %13
+ OpBranch %114
+ %114 = OpLabel
+ %14 = OpLoad %6 %8
+ OpStore %11 %14
+ OpBranch %115
+ %115 = OpLabel
+ %15 = OpLoad %6 %8
+ OpStore %11 %15
+ OpBranch %116
+ %116 = OpLabel
+ %16 = OpLoad %6 %8
+ OpStore %11 %16
+ %17 = OpLoad %6 %8
+ OpStore %11 %17
+ %18 = OpLoad %6 %8
+ OpStore %11 %18
+ %19 = OpLoad %6 %8
+ OpStore %11 %19
+ %20 = OpLoad %6 %8
+ OpStore %11 %20
+ %21 = OpLoad %6 %8
+ OpStore %11 %21
+ %22 = OpLoad %6 %8
+ OpStore %11 %22
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, kHalfSplitShader, binary_out));
+ }
+
+ {
+ // Empty replay
+ protobufs::TransformationSequence transformations_out;
+ protobufs::FactSequence empty_facts;
+ std::vector<uint32_t> binary_out;
+ Replayer replayer(env, true, validator_options);
+ replayer.SetMessageConsumer(kSilentConsumer);
+ auto replayer_result_status =
+ replayer.Run(binary_in, empty_facts, transformations, 0, &binary_out,
+ &transformations_out);
+ // Replay should succeed.
+ ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
+ replayer_result_status);
+ // No transformations should be applied
+ ASSERT_EQ(0, transformations_out.transformation_size());
+ ASSERT_TRUE(IsEqual(env, kTestShader, binary_out));
+ }
+
+ {
+ // Invalid replay: too many transformations
+ protobufs::TransformationSequence transformations_out;
+ protobufs::FactSequence empty_facts;
+ std::vector<uint32_t> binary_out;
+ // The number of transformations requested to be applied exceeds the number
+ // of transformations
+ Replayer replayer(env, true, validator_options);
+ replayer.SetMessageConsumer(kSilentConsumer);
+ auto replayer_result_status =
+ replayer.Run(binary_in, empty_facts, transformations, 12, &binary_out,
+ &transformations_out);
+
+ // Replay should not succeed.
+ ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested,
+ replayer_result_status);
+ // No transformations should be applied
+ ASSERT_EQ(0, transformations_out.transformation_size());
+ // The output binary should be empty
+ ASSERT_TRUE(binary_out.empty());
+ }
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
index 443c31c..adb14e3 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_access_chain_test.cpp
@@ -150,7 +150,7 @@
100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
.IsApplicable(context.get(), transformation_context));
- // Bad: index id is not a constant
+ // Bad: index id is not a constant and the pointer refers to a struct
ASSERT_FALSE(TransformationAccessChain(
100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0))
.IsApplicable(context.get(), transformation_context));
@@ -161,9 +161,9 @@
MakeInstructionDescriptor(24, SpvOpLoad, 0))
.IsApplicable(context.get(), transformation_context));
- // Bad: index id is out of bounds
+ // Bad: index id is out of bounds when accessing a struct
ASSERT_FALSE(
- TransformationAccessChain(100, 43, {80, 83},
+ TransformationAccessChain(100, 43, {83, 80},
MakeInstructionDescriptor(24, SpvOpLoad, 0))
.IsApplicable(context.get(), transformation_context));
@@ -172,6 +172,12 @@
100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0))
.IsApplicable(context.get(), transformation_context));
+ // Bad: OpTypeBool must be present in the module to clamp an index
+ ASSERT_FALSE(
+ TransformationAccessChain(100, 36, {80, 81},
+ MakeInstructionDescriptor(37, SpvOpStore, 0))
+ .IsApplicable(context.get(), transformation_context));
+
// Bad: pointer not available
ASSERT_FALSE(
TransformationAccessChain(
@@ -183,15 +189,23 @@
100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100))
.IsApplicable(context.get(), transformation_context));
+#ifndef NDEBUG
// Bad: pointer is null
- ASSERT_FALSE(TransformationAccessChain(
- 100, 45, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_DEATH(
+ TransformationAccessChain(100, 45, {80},
+ MakeInstructionDescriptor(24, SpvOpLoad, 0))
+ .IsApplicable(context.get(), transformation_context),
+ "Access chains should not be created from null/undefined pointers");
+#endif
+#ifndef NDEBUG
// Bad: pointer is undef
- ASSERT_FALSE(TransformationAccessChain(
- 100, 46, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0))
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_DEATH(
+ TransformationAccessChain(100, 46, {80},
+ MakeInstructionDescriptor(24, SpvOpLoad, 0))
+ .IsApplicable(context.get(), transformation_context),
+ "Access chains should not be created from null/undefined pointers");
+#endif
// Bad: pointer to result type does not exist
ASSERT_FALSE(TransformationAccessChain(
@@ -222,18 +236,7 @@
{
TransformationAccessChain transformation(
- 102, 36, {80, 81}, MakeInstructionDescriptor(37, SpvOpStore, 0));
- ASSERT_TRUE(
- transformation.IsApplicable(context.get(), transformation_context));
- transformation.Apply(context.get(), &transformation_context);
- ASSERT_TRUE(IsValid(env, context.get()));
- ASSERT_FALSE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
- }
-
- {
- TransformationAccessChain transformation(
- 103, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
+ 102, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -244,7 +247,7 @@
{
TransformationAccessChain transformation(
- 104, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
+ 103, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -255,7 +258,7 @@
{
TransformationAccessChain transformation(
- 105, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
+ 104, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -266,7 +269,7 @@
{
TransformationAccessChain transformation(
- 106, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
+ 105, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -277,7 +280,7 @@
{
TransformationAccessChain transformation(
- 107, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
+ 106, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -286,28 +289,6 @@
transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107));
}
- {
- TransformationAccessChain transformation(
- 108, 54, {85, 81, 81}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
- ASSERT_TRUE(
- transformation.IsApplicable(context.get(), transformation_context));
- transformation.Apply(context.get(), &transformation_context);
- ASSERT_TRUE(IsValid(env, context.get()));
- ASSERT_TRUE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(108));
- }
-
- {
- TransformationAccessChain transformation(
- 109, 48, {80, 80}, MakeInstructionDescriptor(24, SpvOpLoad, 0));
- ASSERT_TRUE(
- transformation.IsApplicable(context.get(), transformation_context));
- transformation.Apply(context.get(), &transformation_context);
- ASSERT_TRUE(IsValid(env, context.get()));
- ASSERT_FALSE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(109));
- }
-
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -359,16 +340,15 @@
%36 = OpVariable %9 Function
%38 = OpVariable %11 Function
%44 = OpCopyObject %9 %36
- %103 = OpAccessChain %9 %44
+ %102 = OpAccessChain %9 %44
OpStore %28 %33
- %105 = OpAccessChain %11 %34
+ %104 = OpAccessChain %11 %34
OpStore %34 %35
%37 = OpLoad %8 %28
- %102 = OpAccessChain %20 %36 %80 %81
OpStore %36 %37
%39 = OpLoad %10 %34
OpStore %38 %39
- %106 = OpAccessChain %11 %38
+ %105 = OpAccessChain %11 %38
%40 = OpFunctionCall %10 %15 %36 %38
%41 = OpLoad %10 %34
%42 = OpIAdd %10 %41 %40
@@ -380,15 +360,13 @@
%13 = OpFunctionParameter %9
%14 = OpFunctionParameter %11
%16 = OpLabel
- %104 = OpAccessChain %70 %13 %80
+ %103 = OpAccessChain %70 %13 %80
%21 = OpAccessChain %20 %13 %17 %19
%43 = OpCopyObject %9 %13
%22 = OpLoad %6 %21
%23 = OpConvertFToS %10 %22
%100 = OpAccessChain %70 %43 %80
- %107 = OpAccessChain %11 %14
- %108 = OpAccessChain %99 %54 %85 %81 %81
- %109 = OpAccessChain %99 %48 %80 %80
+ %106 = OpAccessChain %11 %14
%24 = OpLoad %10 %14
%25 = OpIAdd %10 %23 %24
OpReturnValue %25
@@ -473,6 +451,237 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationAccessChainTest, ClampingVariables) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main" %3
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %4 = OpTypeVoid
+ %5 = OpTypeBool
+ %6 = OpTypeFunction %4
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %7 4
+ %9 = OpTypePointer Function %8
+ %10 = OpConstant %7 0
+ %11 = OpConstant %7 1
+ %12 = OpConstant %7 3
+ %13 = OpConstant %7 2
+ %14 = OpConstantComposite %8 %10 %11 %12 %13
+ %15 = OpTypePointer Function %7
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 1
+ %18 = OpConstant %16 3
+ %19 = OpTypeStruct %8
+ %20 = OpTypePointer Function %19
+ %21 = OpConstant %7 9
+ %22 = OpConstant %16 10
+ %23 = OpTypeArray %19 %22
+ %24 = OpTypePointer Function %23
+ %25 = OpTypeFloat 32
+ %26 = OpTypeVector %25 4
+ %27 = OpTypePointer Output %26
+ %3 = OpVariable %27 Output
+ %2 = OpFunction %4 None %6
+ %28 = OpLabel
+ %29 = OpVariable %9 Function
+ %30 = OpVariable %15 Function
+ %31 = OpVariable %15 Function
+ %32 = OpVariable %20 Function
+ %33 = OpVariable %15 Function
+ %34 = OpVariable %24 Function
+ OpStore %29 %14
+ OpStore %30 %10
+ %36 = OpLoad %7 %30
+ %38 = OpLoad %8 %29
+ %39 = OpCompositeConstruct %19 %38
+ %40 = OpLoad %7 %30
+ %42 = OpLoad %8 %29
+ %43 = OpCompositeConstruct %19 %42
+ %45 = OpLoad %7 %30
+ %46 = OpLoad %7 %33
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Bad: no ids given for clamping
+ ASSERT_FALSE(TransformationAccessChain(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: an id given for clamping is not fresh
+ ASSERT_FALSE(TransformationAccessChain(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+ {{46, 201}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: an id given for clamping is not fresh
+ ASSERT_FALSE(TransformationAccessChain(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+ {{200, 46}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: an id given for clamping is the same as the id for the access chain
+ ASSERT_FALSE(TransformationAccessChain(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+ {{100, 201}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: the fresh ids given are not distinct
+ ASSERT_FALSE(TransformationAccessChain(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+ {{200, 200}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: not enough ids given for clamping (2 pairs needed)
+ ASSERT_FALSE(
+ TransformationAccessChain(104, 34, {45, 10, 46},
+ MakeInstructionDescriptor(46, SpvOpReturn, 0),
+ {{208, 209}, {209, 211}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Bad: the fresh ids given are not distinct
+ ASSERT_FALSE(
+ TransformationAccessChain(104, 34, {45, 10, 46},
+ MakeInstructionDescriptor(46, SpvOpReturn, 0),
+ {{208, 209}, {209, 211}})
+ .IsApplicable(context.get(), transformation_context));
+
+ {
+ TransformationAccessChain transformation(
+ 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0),
+ {{200, 201}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ {
+ TransformationAccessChain transformation(
+ 101, 29, {36}, MakeInstructionDescriptor(38, SpvOpLoad, 0),
+ {{202, 203}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ {
+ TransformationAccessChain transformation(
+ 102, 32, {10, 40}, MakeInstructionDescriptor(42, SpvOpLoad, 0),
+ {{204, 205}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ {
+ TransformationAccessChain transformation(
+ 103, 34, {11}, MakeInstructionDescriptor(45, SpvOpLoad, 0),
+ {{206, 207}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ {
+ TransformationAccessChain transformation(
+ 104, 34, {45, 10, 46}, MakeInstructionDescriptor(46, SpvOpReturn, 0),
+ {{208, 209}, {210, 211}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main" %3
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %4 = OpTypeVoid
+ %5 = OpTypeBool
+ %6 = OpTypeFunction %4
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %7 4
+ %9 = OpTypePointer Function %8
+ %10 = OpConstant %7 0
+ %11 = OpConstant %7 1
+ %12 = OpConstant %7 3
+ %13 = OpConstant %7 2
+ %14 = OpConstantComposite %8 %10 %11 %12 %13
+ %15 = OpTypePointer Function %7
+ %16 = OpTypeInt 32 0
+ %17 = OpConstant %16 1
+ %18 = OpConstant %16 3
+ %19 = OpTypeStruct %8
+ %20 = OpTypePointer Function %19
+ %21 = OpConstant %7 9
+ %22 = OpConstant %16 10
+ %23 = OpTypeArray %19 %22
+ %24 = OpTypePointer Function %23
+ %25 = OpTypeFloat 32
+ %26 = OpTypeVector %25 4
+ %27 = OpTypePointer Output %26
+ %3 = OpVariable %27 Output
+ %2 = OpFunction %4 None %6
+ %28 = OpLabel
+ %29 = OpVariable %9 Function
+ %30 = OpVariable %15 Function
+ %31 = OpVariable %15 Function
+ %32 = OpVariable %20 Function
+ %33 = OpVariable %15 Function
+ %34 = OpVariable %24 Function
+ OpStore %29 %14
+ OpStore %30 %10
+ %200 = OpULessThanEqual %5 %17 %18
+ %201 = OpSelect %16 %200 %17 %18
+ %100 = OpAccessChain %15 %29 %201
+ %36 = OpLoad %7 %30
+ %202 = OpULessThanEqual %5 %36 %12
+ %203 = OpSelect %7 %202 %36 %12
+ %101 = OpAccessChain %15 %29 %203
+ %38 = OpLoad %8 %29
+ %39 = OpCompositeConstruct %19 %38
+ %40 = OpLoad %7 %30
+ %204 = OpULessThanEqual %5 %40 %12
+ %205 = OpSelect %7 %204 %40 %12
+ %102 = OpAccessChain %15 %32 %10 %205
+ %42 = OpLoad %8 %29
+ %43 = OpCompositeConstruct %19 %42
+ %206 = OpULessThanEqual %5 %11 %21
+ %207 = OpSelect %7 %206 %11 %21
+ %103 = OpAccessChain %20 %34 %207
+ %45 = OpLoad %7 %30
+ %46 = OpLoad %7 %33
+ %208 = OpULessThanEqual %5 %45 %21
+ %209 = OpSelect %7 %208 %45 %21
+ %210 = OpULessThanEqual %5 %46 %12
+ %211 = OpSelect %7 %210 %46 %12
+ %104 = OpAccessChain %15 %34 %209 %10 %211
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
index c603333..1a40329 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_boolean_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_add_constant_boolean.h"
+
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -48,17 +49,23 @@
validator_options);
// True and false can both be added as neither is present.
- ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable(
- context.get(), transformation_context));
- ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable(
- context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddConstantBoolean(7, true, false)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddConstantBoolean(7, false, false)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Irrelevant true and false can both be added as neither is present.
+ ASSERT_TRUE(TransformationAddConstantBoolean(7, true, true)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddConstantBoolean(7, false, true)
+ .IsApplicable(context.get(), transformation_context));
// Id 5 is already taken.
- ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable(
- context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddConstantBoolean(5, true, false)
+ .IsApplicable(context.get(), transformation_context));
- auto add_true = TransformationAddConstantBoolean(7, true);
- auto add_false = TransformationAddConstantBoolean(8, false);
+ auto add_true = TransformationAddConstantBoolean(7, true, false);
+ auto add_false = TransformationAddConstantBoolean(8, false, false);
ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context));
add_true.Apply(context.get(), &transformation_context);
@@ -67,7 +74,7 @@
// Having added true, we cannot add it again with the same id.
ASSERT_FALSE(add_true.IsApplicable(context.get(), transformation_context));
// But we can add it with a different id.
- auto add_true_again = TransformationAddConstantBoolean(100, true);
+ auto add_true_again = TransformationAddConstantBoolean(100, true, false);
ASSERT_TRUE(
add_true_again.IsApplicable(context.get(), transformation_context));
add_true_again.Apply(context.get(), &transformation_context);
@@ -80,12 +87,31 @@
// Having added false, we cannot add it again with the same id.
ASSERT_FALSE(add_false.IsApplicable(context.get(), transformation_context));
// But we can add it with a different id.
- auto add_false_again = TransformationAddConstantBoolean(101, false);
+ auto add_false_again = TransformationAddConstantBoolean(101, false, false);
ASSERT_TRUE(
add_false_again.IsApplicable(context.get(), transformation_context));
add_false_again.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
+ // We can create an irrelevant OpConstantTrue.
+ TransformationAddConstantBoolean irrelevant_true(102, true, true);
+ ASSERT_TRUE(
+ irrelevant_true.IsApplicable(context.get(), transformation_context));
+ irrelevant_true.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // We can create an irrelevant OpConstantFalse.
+ TransformationAddConstantBoolean irrelevant_false(103, false, true);
+ ASSERT_TRUE(
+ irrelevant_false.IsApplicable(context.get(), transformation_context));
+ irrelevant_false.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(100));
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(101));
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(102));
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(103));
+
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -101,6 +127,8 @@
%100 = OpConstantTrue %6
%8 = OpConstantFalse %6
%101 = OpConstantFalse %6
+ %102 = OpConstantTrue %6
+ %103 = OpConstantFalse %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
@@ -138,10 +166,16 @@
validator_options);
// Neither true nor false can be added as OpTypeBool is not present.
- ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable(
- context.get(), transformation_context));
- ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable(
- context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddConstantBoolean(6, true, false)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddConstantBoolean(6, false, false)
+ .IsApplicable(context.get(), transformation_context));
+
+ // This does not depend on whether the constant is relevant or not.
+ ASSERT_FALSE(TransformationAddConstantBoolean(6, true, true)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddConstantBoolean(6, false, true)
+ .IsApplicable(context.get(), transformation_context));
}
} // namespace
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
index 021bf58..75e23ad 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_composite_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_add_constant_composite.h"
+
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -69,39 +70,62 @@
validator_options);
// Too few ids
- ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101})
+ ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101}, false)
.IsApplicable(context.get(), transformation_context));
// Too many ids
- ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14})
+ ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14}, false)
.IsApplicable(context.get(), transformation_context));
// Id already in use
- ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12})
+ ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12}, false)
.IsApplicable(context.get(), transformation_context));
// %39 is not a type
- ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12})
+ ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}, false)
.IsApplicable(context.get(), transformation_context));
TransformationAddConstantComposite transformations[] = {
// %100 = OpConstantComposite %7 %11 %12
- TransformationAddConstantComposite(100, 7, {11, 12}),
+ TransformationAddConstantComposite(100, 7, {11, 12}, false),
// %101 = OpConstantComposite %7 %14 %15
- TransformationAddConstantComposite(101, 7, {14, 15}),
+ TransformationAddConstantComposite(101, 7, {14, 15}, false),
// %102 = OpConstantComposite %7 %17 %18
- TransformationAddConstantComposite(102, 7, {17, 18}),
+ TransformationAddConstantComposite(102, 7, {17, 18}, false),
// %103 = OpConstantComposite %8 %100 %101 %102
- TransformationAddConstantComposite(103, 8, {100, 101, 102}),
+ TransformationAddConstantComposite(103, 8, {100, 101, 102}, false),
// %104 = OpConstantComposite %24 %29 %30 %31
- TransformationAddConstantComposite(104, 24, {29, 30, 31}),
+ TransformationAddConstantComposite(104, 24, {29, 30, 31}, false),
// %105 = OpConstantComposite %26 %104 %33
- TransformationAddConstantComposite(105, 26, {104, 33}),
+ TransformationAddConstantComposite(105, 26, {104, 33}, false),
// %106 = OpConstantComposite %35 %38 %39 %40
- TransformationAddConstantComposite(106, 35, {38, 39, 40})};
+ TransformationAddConstantComposite(106, 35, {38, 39, 40}, false),
+
+ // Same constants but with an irrelevant fact applied.
+
+ // %107 = OpConstantComposite %7 %11 %12
+ TransformationAddConstantComposite(107, 7, {11, 12}, true),
+
+ // %108 = OpConstantComposite %7 %14 %15
+ TransformationAddConstantComposite(108, 7, {14, 15}, true),
+
+ // %109 = OpConstantComposite %7 %17 %18
+ TransformationAddConstantComposite(109, 7, {17, 18}, true),
+
+ // %110 = OpConstantComposite %8 %100 %101 %102
+ TransformationAddConstantComposite(110, 8, {100, 101, 102}, true),
+
+ // %111 = OpConstantComposite %24 %29 %30 %31
+ TransformationAddConstantComposite(111, 24, {29, 30, 31}, true),
+
+ // %112 = OpConstantComposite %26 %104 %33
+ TransformationAddConstantComposite(112, 26, {104, 33}, true),
+
+ // %113 = OpConstantComposite %35 %38 %39 %40
+ TransformationAddConstantComposite(113, 35, {38, 39, 40}, true)};
for (auto& transformation : transformations) {
ASSERT_TRUE(
@@ -110,6 +134,14 @@
}
ASSERT_TRUE(IsValid(env, context.get()));
+ for (uint32_t id = 100; id <= 106; ++id) {
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(id));
+ }
+
+ for (uint32_t id = 107; id <= 113; ++id) {
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(id));
+ }
+
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -149,6 +181,13 @@
%104 = OpConstantComposite %24 %29 %30 %31
%105 = OpConstantComposite %26 %104 %33
%106 = OpConstantComposite %35 %38 %39 %40
+ %107 = OpConstantComposite %7 %11 %12
+ %108 = OpConstantComposite %7 %14 %15
+ %109 = OpConstantComposite %7 %17 %18
+ %110 = OpConstantComposite %8 %100 %101 %102
+ %111 = OpConstantComposite %24 %29 %30 %31
+ %112 = OpConstantComposite %26 %104 %33
+ %113 = OpConstantComposite %35 %38 %39 %40
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
index 5124b7d..7d9608d 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_constant_scalar_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_add_constant_scalar.h"
+
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -70,24 +71,42 @@
uint32_t uint_for_float[2];
memcpy(uint_for_float, float_values, sizeof(float_values));
- auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1});
- auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10});
- auto add_unsigned_int_2 = TransformationAddConstantScalar(102, 10, {2});
- auto add_unsigned_int_20 = TransformationAddConstantScalar(103, 10, {20});
+ auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1}, false);
+ auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10}, false);
+ auto add_unsigned_int_2 =
+ TransformationAddConstantScalar(102, 10, {2}, false);
+ auto add_unsigned_int_20 =
+ TransformationAddConstantScalar(103, 10, {20}, false);
auto add_float_3 =
- TransformationAddConstantScalar(104, 14, {uint_for_float[0]});
+ TransformationAddConstantScalar(104, 14, {uint_for_float[0]}, false);
auto add_float_30 =
- TransformationAddConstantScalar(105, 14, {uint_for_float[1]});
+ TransformationAddConstantScalar(105, 14, {uint_for_float[1]}, false);
+ auto add_signed_int_1_irrelevant =
+ TransformationAddConstantScalar(106, 6, {1}, true);
+ auto add_signed_int_10_irrelevant =
+ TransformationAddConstantScalar(107, 6, {10}, true);
+ auto add_unsigned_int_2_irrelevant =
+ TransformationAddConstantScalar(108, 10, {2}, true);
+ auto add_unsigned_int_20_irrelevant =
+ TransformationAddConstantScalar(109, 10, {20}, true);
+ auto add_float_3_irrelevant =
+ TransformationAddConstantScalar(110, 14, {uint_for_float[0]}, true);
+ auto add_float_30_irrelevant =
+ TransformationAddConstantScalar(111, 14, {uint_for_float[1]}, true);
auto bad_add_float_30_id_already_used =
- TransformationAddConstantScalar(104, 14, {uint_for_float[1]});
- auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1});
- auto bad_no_data = TransformationAddConstantScalar(100, 6, {});
- auto bad_too_much_data = TransformationAddConstantScalar(100, 6, {1, 2});
+ TransformationAddConstantScalar(104, 14, {uint_for_float[1]}, false);
+ auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1}, false);
+ auto bad_no_data = TransformationAddConstantScalar(100, 6, {}, false);
+ auto bad_too_much_data =
+ TransformationAddConstantScalar(100, 6, {1, 2}, false);
auto bad_type_id_does_not_exist =
- TransformationAddConstantScalar(108, 2020, {uint_for_float[0]});
- auto bad_type_id_is_not_a_type = TransformationAddConstantScalar(109, 9, {0});
- auto bad_type_id_is_void = TransformationAddConstantScalar(110, 2, {0});
- auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0});
+ TransformationAddConstantScalar(108, 2020, {uint_for_float[0]}, false);
+ auto bad_type_id_is_not_a_type =
+ TransformationAddConstantScalar(109, 9, {0}, false);
+ auto bad_type_id_is_void =
+ TransformationAddConstantScalar(110, 2, {0}, false);
+ auto bad_type_id_is_pointer =
+ TransformationAddConstantScalar(111, 11, {0}, false);
// Id is already in use.
ASSERT_FALSE(
@@ -144,9 +163,48 @@
add_float_30.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
+ // Add irrelevant ids.
+ ASSERT_TRUE(add_signed_int_1_irrelevant.IsApplicable(context.get(),
+ transformation_context));
+ add_signed_int_1_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(add_signed_int_10_irrelevant.IsApplicable(
+ context.get(), transformation_context));
+ add_signed_int_10_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(add_unsigned_int_2_irrelevant.IsApplicable(
+ context.get(), transformation_context));
+ add_unsigned_int_2_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(add_unsigned_int_20_irrelevant.IsApplicable(
+ context.get(), transformation_context));
+ add_unsigned_int_20_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(add_float_3_irrelevant.IsApplicable(context.get(),
+ transformation_context));
+ add_float_3_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(add_float_30_irrelevant.IsApplicable(context.get(),
+ transformation_context));
+ add_float_30_irrelevant.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(
context.get(), transformation_context));
+ for (uint32_t id = 100; id <= 105; ++id) {
+ ASSERT_FALSE(fact_manager.IdIsIrrelevant(id));
+ }
+
+ for (uint32_t id = 106; id <= 111; ++id) {
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(id));
+ }
+
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@@ -177,6 +235,12 @@
%103 = OpConstant %10 20
%104 = OpConstant %14 3
%105 = OpConstant %14 30
+ %106 = OpConstant %6 1
+ %107 = OpConstant %6 10
+ %108 = OpConstant %10 2
+ %109 = OpConstant %10 20
+ %110 = OpConstant %14 3
+ %111 = OpConstant %14 30
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp
new file mode 100644
index 0000000..66a15f4
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -0,0 +1,384 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_copy_memory.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddCopyMemoryTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %19 RelaxedPrecision
+ OpMemberDecorate %66 0 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypePointer Function %6
+ %78 = OpTypePointer Private %6
+ %8 = OpTypeFunction %6 %7
+ %17 = OpTypeInt 32 1
+ %18 = OpTypePointer Function %17
+ %79 = OpTypePointer Private %17
+ %20 = OpConstant %17 0
+ %21 = OpTypeFloat 32
+ %22 = OpTypePointer Function %21
+ %80 = OpTypePointer Private %21
+ %24 = OpConstant %21 0
+ %25 = OpConstantFalse %6
+ %32 = OpConstantTrue %6
+ %33 = OpTypeVector %21 4
+ %34 = OpTypePointer Function %33
+ %81 = OpTypePointer Private %33
+ %36 = OpConstantComposite %33 %24 %24 %24 %24
+ %37 = OpTypeMatrix %33 4
+ %84 = OpConstantComposite %37 %36 %36 %36 %36
+ %38 = OpTypePointer Function %37
+ %82 = OpTypePointer Private %37
+ %44 = OpConstant %21 1
+ %66 = OpTypeStruct %17 %21 %6 %33 %37
+ %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+ %67 = OpTypePointer Function %66
+ %83 = OpTypePointer Private %66
+ %86 = OpVariable %79 Private %20
+ %87 = OpUndef %79
+ %88 = OpConstantNull %79
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %19 = OpVariable %18 Function
+ %23 = OpVariable %22 Function
+ %26 = OpVariable %7 Function
+ %30 = OpVariable %7 Function
+ %35 = OpVariable %34 Function
+ %39 = OpVariable %38 Function
+ %68 = OpVariable %67 Function
+ OpStore %19 %20
+ OpStore %23 %24
+ OpStore %26 %25
+ %27 = OpFunctionCall %6 %10 %26
+ OpSelectionMerge %29 None
+ OpBranchConditional %27 %28 %31
+ %28 = OpLabel
+ OpBranch %29
+ %31 = OpLabel
+ OpBranch %29
+ %76 = OpLabel
+ %77 = OpLogicalEqual %6 %25 %32
+ OpBranch %29
+ %29 = OpLabel
+ %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+ OpStore %30 %75
+ %40 = OpLoad %33 %35
+ %41 = OpLoad %33 %35
+ %42 = OpLoad %33 %35
+ %43 = OpLoad %33 %35
+ %45 = OpCompositeExtract %21 %40 0
+ %46 = OpCompositeExtract %21 %40 1
+ %47 = OpCompositeExtract %21 %40 2
+ %48 = OpCompositeExtract %21 %40 3
+ %49 = OpCompositeExtract %21 %41 0
+ %50 = OpCompositeExtract %21 %41 1
+ %51 = OpCompositeExtract %21 %41 2
+ %52 = OpCompositeExtract %21 %41 3
+ %53 = OpCompositeExtract %21 %42 0
+ %54 = OpCompositeExtract %21 %42 1
+ %55 = OpCompositeExtract %21 %42 2
+ %56 = OpCompositeExtract %21 %42 3
+ %57 = OpCompositeExtract %21 %43 0
+ %58 = OpCompositeExtract %21 %43 1
+ %59 = OpCompositeExtract %21 %43 2
+ %60 = OpCompositeExtract %21 %43 3
+ %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+ %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+ %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+ %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+ %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+ OpStore %39 %65
+ %69 = OpLoad %17 %19
+ %70 = OpLoad %21 %23
+ %71 = OpLoad %6 %30
+ %72 = OpLoad %33 %35
+ %73 = OpLoad %37 %39
+ %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+ OpStore %68 %74
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %12 = OpVariable %7 Function
+ %13 = OpLoad %6 %9
+ OpStore %12 %13
+ %14 = OpLoad %6 %12
+ OpReturnValue %14
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Target id is not fresh (59).
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 59, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Instruction descriptor is invalid (id 89 is undefined).
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(89, SpvOpVariable, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Cannot insert OpCopyMemory before OpPhi.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(75, SpvOpPhi, 0),
+ 89, 19, SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is invalid.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 76,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction's type doesn't exist.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 5,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction's type is invalid.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 40, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is OpUndef.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 87, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction is OpConstantNull.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0),
+ 89, 88, SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Storage class is invalid.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassWorkgroup, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Initializer is 0.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassPrivate, 0)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Initializer has wrong type.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 89, 19,
+ SpvStorageClassPrivate, 25)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source and target instructions are in different functions.
+ ASSERT_FALSE(
+ TransformationAddCopyMemory(MakeInstructionDescriptor(13, SpvOpLoad, 0),
+ 89, 19, SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source instruction doesn't dominate the target instruction.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(77, SpvOpLogicalEqual, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Source and target instructions are the same.
+ ASSERT_FALSE(TransformationAddCopyMemory(
+ MakeInstructionDescriptor(19, SpvOpVariable, 0), 89, 19,
+ SpvStorageClassPrivate, 20)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Correct transformations.
+ uint32_t fresh_id = 89;
+ auto descriptor = MakeInstructionDescriptor(27, SpvOpFunctionCall, 0);
+ std::vector<uint32_t> source_ids = {19, 23, 26, 30, 35, 39, 68, 86};
+ std::vector<uint32_t> initializers = {20, 24, 25, 25, 36, 84, 85, 20};
+ std::vector<SpvStorageClass> storage_classes = {SpvStorageClassPrivate,
+ SpvStorageClassFunction};
+ for (size_t i = 0, n = source_ids.size(); i < n; ++i) {
+ TransformationAddCopyMemory transformation(
+ descriptor, fresh_id, source_ids[i],
+ storage_classes[i % storage_classes.size()], initializers[i]);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(fresh_id));
+ fresh_id++;
+ }
+
+ std::string expected = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %19 RelaxedPrecision
+ OpMemberDecorate %66 0 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypePointer Function %6
+ %78 = OpTypePointer Private %6
+ %8 = OpTypeFunction %6 %7
+ %17 = OpTypeInt 32 1
+ %18 = OpTypePointer Function %17
+ %79 = OpTypePointer Private %17
+ %20 = OpConstant %17 0
+ %21 = OpTypeFloat 32
+ %22 = OpTypePointer Function %21
+ %80 = OpTypePointer Private %21
+ %24 = OpConstant %21 0
+ %25 = OpConstantFalse %6
+ %32 = OpConstantTrue %6
+ %33 = OpTypeVector %21 4
+ %34 = OpTypePointer Function %33
+ %81 = OpTypePointer Private %33
+ %36 = OpConstantComposite %33 %24 %24 %24 %24
+ %37 = OpTypeMatrix %33 4
+ %84 = OpConstantComposite %37 %36 %36 %36 %36
+ %38 = OpTypePointer Function %37
+ %82 = OpTypePointer Private %37
+ %44 = OpConstant %21 1
+ %66 = OpTypeStruct %17 %21 %6 %33 %37
+ %85 = OpConstantComposite %66 %20 %24 %25 %36 %84
+ %67 = OpTypePointer Function %66
+ %83 = OpTypePointer Private %66
+ %86 = OpVariable %79 Private %20
+ %87 = OpUndef %79
+ %88 = OpConstantNull %79
+ %89 = OpVariable %79 Private %20
+ %91 = OpVariable %78 Private %25
+ %93 = OpVariable %81 Private %36
+ %95 = OpVariable %83 Private %85
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %96 = OpVariable %18 Function %20
+ %94 = OpVariable %38 Function %84
+ %92 = OpVariable %7 Function %25
+ %90 = OpVariable %22 Function %24
+ %19 = OpVariable %18 Function
+ %23 = OpVariable %22 Function
+ %26 = OpVariable %7 Function
+ %30 = OpVariable %7 Function
+ %35 = OpVariable %34 Function
+ %39 = OpVariable %38 Function
+ %68 = OpVariable %67 Function
+ OpStore %19 %20
+ OpStore %23 %24
+ OpStore %26 %25
+ OpCopyMemory %89 %19
+ OpCopyMemory %90 %23
+ OpCopyMemory %91 %26
+ OpCopyMemory %92 %30
+ OpCopyMemory %93 %35
+ OpCopyMemory %94 %39
+ OpCopyMemory %95 %68
+ OpCopyMemory %96 %86
+ %27 = OpFunctionCall %6 %10 %26
+ OpSelectionMerge %29 None
+ OpBranchConditional %27 %28 %31
+ %28 = OpLabel
+ OpBranch %29
+ %31 = OpLabel
+ OpBranch %29
+ %76 = OpLabel
+ %77 = OpLogicalEqual %6 %25 %32
+ OpBranch %29
+ %29 = OpLabel
+ %75 = OpPhi %6 %25 %31 %32 %28 %77 %76
+ OpStore %30 %75
+ %40 = OpLoad %33 %35
+ %41 = OpLoad %33 %35
+ %42 = OpLoad %33 %35
+ %43 = OpLoad %33 %35
+ %45 = OpCompositeExtract %21 %40 0
+ %46 = OpCompositeExtract %21 %40 1
+ %47 = OpCompositeExtract %21 %40 2
+ %48 = OpCompositeExtract %21 %40 3
+ %49 = OpCompositeExtract %21 %41 0
+ %50 = OpCompositeExtract %21 %41 1
+ %51 = OpCompositeExtract %21 %41 2
+ %52 = OpCompositeExtract %21 %41 3
+ %53 = OpCompositeExtract %21 %42 0
+ %54 = OpCompositeExtract %21 %42 1
+ %55 = OpCompositeExtract %21 %42 2
+ %56 = OpCompositeExtract %21 %42 3
+ %57 = OpCompositeExtract %21 %43 0
+ %58 = OpCompositeExtract %21 %43 1
+ %59 = OpCompositeExtract %21 %43 2
+ %60 = OpCompositeExtract %21 %43 3
+ %61 = OpCompositeConstruct %33 %45 %46 %47 %48
+ %62 = OpCompositeConstruct %33 %49 %50 %51 %52
+ %63 = OpCompositeConstruct %33 %53 %54 %55 %56
+ %64 = OpCompositeConstruct %33 %57 %58 %59 %60
+ %65 = OpCompositeConstruct %37 %61 %62 %63 %64
+ OpStore %39 %65
+ %69 = OpLoad %17 %19
+ %70 = OpLoad %21 %23
+ %71 = OpLoad %6 %30
+ %72 = OpLoad %33 %35
+ %73 = OpLoad %37 %39
+ %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73
+ OpStore %68 %74
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %12 = OpVariable %7 Function
+ %13 = OpLoad %6 %9
+ OpStore %12 %13
+ %14 = OpLoad %6 %12
+ OpReturnValue %14
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
index 8400b0c..19fac35 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_dead_break_test.cpp
@@ -1196,15 +1196,15 @@
TransformationAddDeadBreak(header_for_j, merge_do_while, true, {})
.IsApplicable(context.get(), transformation_context));
- // Not OK to break loop from its continue construct
+ // Not OK to break loop from its continue construct, except from the back-edge
+ // block.
ASSERT_FALSE(
TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {})
.IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(
- TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
- .IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {})
+ .IsApplicable(context.get(), transformation_context));
// Not OK to break out of multiple non-loop constructs if not breaking to a
// loop merge
@@ -1468,11 +1468,117 @@
TransformationContext transformation_context(&fact_manager,
validator_options);
- // Not OK to break loop from its continue construct
+ // Not OK to break loop from its continue construct, except from the back-edge
+ // block.
ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {})
.IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(TransformationAddDeadBreak(23, 12, true, {})
- .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(TransformationAddDeadBreak(23, 12, true, {})
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddDeadBreakTest, BreakFromBackEdgeBlock) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %10 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeBool
+ %6 = OpTypePointer Function %4
+
+; Constants
+ %7 = OpConstant %4 0
+ %8 = OpConstant %4 1
+ %9 = OpConstantTrue %5
+
+; main function
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %12 = OpVariable %6 Function
+ OpStore %12 %7
+ OpBranch %13
+ %13 = OpLabel
+ OpLoopMerge %21 %18 None ; structured loop
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %4 %12
+ %16 = OpULessThan %5 %15 %8 ; i < 1 ?
+ OpBranchConditional %16 %17 %21 ; body or break
+ %17 = OpLabel ; body
+ OpBranch %18
+ %18 = OpLabel ; continue target does not strictly dominates the back-edge block
+ %19 = OpLoad %4 %12
+ %20 = OpIAdd %4 %19 %8 ; ++i
+ OpStore %12 %20
+ OpBranch %13
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto transformation = TransformationAddDeadBreak(18, 21, true, {});
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %10 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeBool
+ %6 = OpTypePointer Function %4
+
+; Constants
+ %7 = OpConstant %4 0
+ %8 = OpConstant %4 1
+ %9 = OpConstantTrue %5
+
+; main function
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %12 = OpVariable %6 Function
+ OpStore %12 %7
+ OpBranch %13
+ %13 = OpLabel
+ OpLoopMerge %21 %18 None ; structured loop
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %4 %12
+ %16 = OpULessThan %5 %15 %8 ; i < 1 ?
+ OpBranchConditional %16 %17 %21 ; body or break
+ %17 = OpLabel ; body
+ OpBranch %18
+ %18 = OpLabel ; continue target does not strictly dominates the back-edge block
+ %19 = OpLoad %4 %12
+ %20 = OpIAdd %4 %19 %8 ; ++i
+ OpStore %12 %20
+ OpBranchConditional %9 %13 %21
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) {
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
new file mode 100644
index 0000000..fc78f9f
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, IsApplicable) {
+ std::string shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %22
+ %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+ %27 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests applicable image instruction.
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ auto transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests undefined image instructions.
+ instruction_descriptor =
+ MakeInstructionDescriptor(27, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(28, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non-image instructions.
+ instruction_descriptor = MakeInstructionDescriptor(19, SpvOpLabel, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLoad, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests coordinate operand being a vec4.
+ instruction_descriptor =
+ MakeInstructionDescriptor(27, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests undefined coordinate with unused operands.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(27, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests coordinate with unused operands being a non-OpCompositeConstruct
+ // instruction.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(21, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests the first OpCompositeConstruct constituent not being the original
+ // coordinate.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, Apply) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %22
+ %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ auto transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %23
+ %26 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp
new file mode 100644
index 0000000..6593d00
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_parameter_test.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_parameter.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddParameterTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %11 = OpTypeInt 32 1
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFunction %7 %7
+ %8 = OpConstant %11 23
+ %12 = OpConstantTrue %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpFunctionCall %7 %9 %12
+ OpReturn
+ OpFunctionEnd
+ %9 = OpFunction %7 None %6
+ %14 = OpFunctionParameter %7
+ %10 = OpLabel
+ OpReturnValue %12
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Can't modify entry point function.
+ ASSERT_FALSE(TransformationAddParameter(4, 15, 12, 16)
+ .IsApplicable(context.get(), transformation_context));
+
+ // There is no function with result id 29.
+ ASSERT_FALSE(TransformationAddParameter(29, 15, 8, 16)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter id is not fresh.
+ ASSERT_FALSE(TransformationAddParameter(9, 14, 8, 16)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Function type id is not fresh.
+ ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 14)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Function type id and parameter type id are equal.
+ ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 15)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter's initializer doesn't exist.
+ ASSERT_FALSE(TransformationAddParameter(9, 15, 15, 16)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Correct transformation.
+ TransformationAddParameter correct(9, 15, 8, 16);
+ ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context));
+ correct.Apply(context.get(), &transformation_context);
+
+ // The module remains valid.
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(fact_manager.IdIsIrrelevant(15));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %7 = OpTypeBool
+ %11 = OpTypeInt 32 1
+ %3 = OpTypeFunction %2
+ %8 = OpConstant %11 23
+ %12 = OpConstantTrue %7
+ %6 = OpTypeFunction %7 %7 %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpFunctionCall %7 %9 %12 %8
+ OpReturn
+ OpFunctionEnd
+ %9 = OpFunction %7 None %6
+ %14 = OpFunctionParameter %7
+ %15 = OpFunctionParameter %11
+ %10 = OpLabel
+ OpReturnValue %12
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp
new file mode 100644
index 0000000..6e163ad
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+TEST(TransformationAddRelaxedDecorationTest, BasicScenarios) {
+ // This is a simple transformation and this test handles the main cases.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 4
+ %11 = OpConstant %6 6
+ %12 = OpTypeBool
+ %13 = OpTypePointer Function %12
+ %15 = OpConstantTrue %12
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpSelectionMerge %19 None
+ OpBranchConditional %15 %19 %100
+ %100 = OpLabel
+ %25 = OpISub %6 %9 %11
+ %28 = OpLogicalNot %12 %15
+ OpBranch %19
+ %19 = OpLabel
+ %27 = OpISub %6 %9 %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactBlockIsDead(100);
+
+ // Invalid: 200 is not an id.
+ ASSERT_FALSE(TransformationAddRelaxedDecoration(200).IsApplicable(
+ context.get(), transformation_context));
+ // Invalid: 27 is not in a dead block.
+ ASSERT_FALSE(TransformationAddRelaxedDecoration(27).IsApplicable(
+ context.get(), transformation_context));
+ // Invalid: 28 is in a dead block, but returns bool (not numeric).
+ ASSERT_FALSE(TransformationAddRelaxedDecoration(28).IsApplicable(
+ context.get(), transformation_context));
+ // It is valid to add RelaxedPrecision to 25 (and it's fine to
+ // have a duplicate).
+ for (uint32_t result_id : {25u, 25u}) {
+ TransformationAddRelaxedDecoration transformation(result_id);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpDecorate %25 RelaxedPrecision
+ OpDecorate %25 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 4
+ %11 = OpConstant %6 6
+ %12 = OpTypeBool
+ %13 = OpTypePointer Function %12
+ %15 = OpConstantTrue %12
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpSelectionMerge %19 None
+ OpBranchConditional %15 %19 %100
+ %100 = OpLabel
+ %25 = OpISub %6 %9 %11
+ %28 = OpLogicalNot %12 %15
+ OpBranch %19
+ %19 = OpLabel
+ %27 = OpISub %6 %9 %11
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_add_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_add_synonym_test.cpp
new file mode 100644
index 0000000..1adc948
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_add_synonym_test.cpp
@@ -0,0 +1,1397 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_synonym.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddSynonymTest, NotApplicable) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %22 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 3
+ %10 = OpTypeFloat 32
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 4.5
+ %14 = OpTypeVector %10 2
+ %15 = OpTypePointer Function %14
+ %17 = OpConstant %10 3
+ %18 = OpConstant %10 4
+ %19 = OpConstantComposite %14 %17 %18
+ %20 = OpTypeVector %6 2
+ %21 = OpTypePointer Function %20
+ %23 = OpConstant %6 4
+ %24 = OpConstantComposite %20 %9 %23
+ %26 = OpConstantNull %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ %22 = OpVariable %21 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %19
+ OpStore %22 %24
+ %25 = OpUndef %6
+ %27 = OpLoad %6 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactIdIsIrrelevant(24);
+
+ auto insert_before = MakeInstructionDescriptor(22, SpvOpReturn, 0);
+
+#ifndef NDEBUG
+ ASSERT_DEATH(
+ TransformationAddSynonym(
+ 9, static_cast<protobufs::TransformationAddSynonym::SynonymType>(-1),
+ 40, insert_before)
+ .IsApplicable(context.get(), transformation_context),
+ "Synonym type is invalid");
+#endif
+
+ // These tests should succeed regardless of the synonym type.
+ for (int i = 0;
+ i < protobufs::TransformationAddSynonym::SynonymType_descriptor()
+ ->value_count();
+ ++i) {
+ const auto* synonym_value =
+ protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i);
+ ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid(
+ synonym_value->number()));
+ auto synonym_type =
+ static_cast<protobufs::TransformationAddSynonym::SynonymType>(
+ synonym_value->number());
+
+ // |synonym_fresh_id| is not fresh.
+ ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // |result_id| is invalid.
+ ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Instruction with |result_id| has no type id.
+ ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Instruction with |result_id| is an OpUndef.
+ ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Instruction with |result_id| is an OpConstantNull.
+ ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // |result_id| is irrelevant.
+ ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // |insert_before| is invalid.
+ ASSERT_FALSE(
+ TransformationAddSynonym(9, synonym_type, 40,
+ MakeInstructionDescriptor(25, SpvOpStore, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Can't insert before |insert_before|.
+ ASSERT_FALSE(
+ TransformationAddSynonym(9, synonym_type, 40,
+ MakeInstructionDescriptor(5, SpvOpLabel, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, synonym_type, 40,
+ MakeInstructionDescriptor(22, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, synonym_type, 40,
+ MakeInstructionDescriptor(25, SpvOpFunctionEnd, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Domination rules are not satisfied.
+ ASSERT_FALSE(
+ TransformationAddSynonym(27, synonym_type, 40,
+ MakeInstructionDescriptor(27, SpvOpLoad, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationAddSynonym(27, synonym_type, 40,
+ MakeInstructionDescriptor(22, SpvOpStore, 1))
+ .IsApplicable(context.get(), transformation_context));
+ }
+}
+
+TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpConstant %6 0
+ %8 = OpConstant %6 1
+ %9 = OpConstant %6 34
+ %10 = OpTypeInt 32 0
+ %13 = OpConstant %10 34
+ %14 = OpTypeFloat 32
+ %15 = OpConstant %14 0
+ %16 = OpConstant %14 1
+ %17 = OpConstant %14 34
+ %18 = OpTypeVector %14 2
+ %19 = OpConstantComposite %18 %15 %15
+ %20 = OpConstantComposite %18 %16 %16
+ %21 = OpConstant %14 3
+ %22 = OpConstant %14 4
+ %23 = OpConstantComposite %18 %21 %22
+ %24 = OpTypeVector %6 2
+ %25 = OpConstantComposite %24 %7 %7
+ %26 = OpConstantComposite %24 %8 %8
+ %27 = OpConstant %6 3
+ %28 = OpConstant %6 4
+ %29 = OpConstantComposite %24 %27 %28
+ %30 = OpTypeVector %10 2
+ %33 = OpConstant %10 3
+ %34 = OpConstant %10 4
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypeBool
+ %37 = OpTypeVector %36 2
+ %38 = OpConstantTrue %36
+ %39 = OpConstantComposite %37 %38 %38
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+
+ uint32_t fresh_id = 50;
+ for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO,
+ protobufs::TransformationAddSynonym::SUB_ZERO,
+ protobufs::TransformationAddSynonym::MUL_ONE}) {
+ ASSERT_TRUE(
+ TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+ // Can't create a synonym of a scalar or a vector of a wrong (in this case -
+ // boolean) type.
+ ASSERT_FALSE(
+ TransformationAddSynonym(38, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationAddSynonym(39, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Required constant is not present in the module.
+ ASSERT_FALSE(
+ TransformationAddSynonym(13, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationAddSynonym(35, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ for (auto result_id : {9, 17, 23, 29}) {
+ TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+ MakeDataDescriptor(fresh_id, {})));
+ ++fresh_id;
+ }
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpConstant %6 0
+ %8 = OpConstant %6 1
+ %9 = OpConstant %6 34
+ %10 = OpTypeInt 32 0
+ %13 = OpConstant %10 34
+ %14 = OpTypeFloat 32
+ %15 = OpConstant %14 0
+ %16 = OpConstant %14 1
+ %17 = OpConstant %14 34
+ %18 = OpTypeVector %14 2
+ %19 = OpConstantComposite %18 %15 %15
+ %20 = OpConstantComposite %18 %16 %16
+ %21 = OpConstant %14 3
+ %22 = OpConstant %14 4
+ %23 = OpConstantComposite %18 %21 %22
+ %24 = OpTypeVector %6 2
+ %25 = OpConstantComposite %24 %7 %7
+ %26 = OpConstantComposite %24 %8 %8
+ %27 = OpConstant %6 3
+ %28 = OpConstant %6 4
+ %29 = OpConstantComposite %24 %27 %28
+ %30 = OpTypeVector %10 2
+ %33 = OpConstant %10 3
+ %34 = OpConstant %10 4
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypeBool
+ %37 = OpTypeVector %36 2
+ %38 = OpConstantTrue %36
+ %39 = OpConstantComposite %37 %38 %38
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %50 = OpIAdd %6 %9 %7
+ %51 = OpFAdd %14 %17 %15
+ %52 = OpFAdd %18 %23 %19
+ %53 = OpIAdd %24 %29 %25
+ %54 = OpISub %6 %9 %7
+ %55 = OpFSub %14 %17 %15
+ %56 = OpFSub %18 %23 %19
+ %57 = OpISub %24 %29 %25
+ %58 = OpIMul %6 %9 %8
+ %59 = OpFMul %14 %17 %16
+ %60 = OpFMul %18 %23 %20
+ %61 = OpIMul %24 %29 %26
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %9 = OpConstantTrue %6
+ %10 = OpTypeVector %6 2
+ %11 = OpConstantComposite %10 %7 %9
+ %12 = OpConstantComposite %10 %7 %7
+ %13 = OpConstantComposite %10 %9 %9
+ %14 = OpTypeFloat 32
+ %17 = OpConstant %14 35
+ %18 = OpTypeVector %14 2
+ %21 = OpConstant %14 3
+ %22 = OpConstant %14 4
+ %23 = OpConstantComposite %18 %21 %22
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+
+ uint32_t fresh_id = 50;
+ for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND,
+ protobufs::TransformationAddSynonym::LOGICAL_OR}) {
+ ASSERT_TRUE(
+ TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+ // Can't create a synonym of a scalar or a vector of a wrong (in this case -
+ // float) type.
+ ASSERT_FALSE(
+ TransformationAddSynonym(17, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationAddSynonym(23, synonym_type, fresh_id, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ for (auto result_id : {9, 11}) {
+ TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+ MakeDataDescriptor(fresh_id, {})));
+ ++fresh_id;
+ }
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %9 = OpConstantTrue %6
+ %10 = OpTypeVector %6 2
+ %11 = OpConstantComposite %10 %7 %9
+ %12 = OpConstantComposite %10 %7 %7
+ %13 = OpConstantComposite %10 %9 %9
+ %14 = OpTypeFloat 32
+ %17 = OpConstant %14 35
+ %18 = OpTypeVector %14 2
+ %21 = OpConstant %14 3
+ %22 = OpConstant %14 4
+ %23 = OpConstantComposite %18 %21 %22
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %50 = OpLogicalAnd %6 %9 %9
+ %51 = OpLogicalAnd %10 %11 %13
+ %52 = OpLogicalOr %6 %9 %7
+ %53 = OpLogicalOr %10 %11 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %10 = OpTypeVector %6 2
+ %12 = OpConstantComposite %10 %7 %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+ const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND;
+
+ // Required constant is not present in the module.
+ ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %10 = OpTypeVector %6 2
+ %12 = OpConstantComposite %10 %7 %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+ const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR;
+
+ // Required constant is not present in the module.
+ ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, CopyObject) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 4
+ %10 = OpTypeFloat 32
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 4
+ %14 = OpTypeVector %10 2
+ %15 = OpTypePointer Function %14
+ %17 = OpConstant %10 3.4000001
+ %18 = OpConstantComposite %14 %17 %17
+ %19 = OpTypeBool
+ %20 = OpTypeStruct %19
+ %21 = OpTypePointer Function %20
+ %23 = OpConstantTrue %19
+ %24 = OpConstantComposite %20 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ %22 = OpVariable %21 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %18
+ OpStore %22 %24
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0);
+ const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT;
+
+ ASSERT_FALSE(
+ TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type));
+
+ uint32_t fresh_id = 50;
+ for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) {
+ TransformationAddSynonym transformation(result_id, synonym_type, fresh_id,
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(result_id, {}),
+ MakeDataDescriptor(fresh_id, {})));
+ ++fresh_id;
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %8 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 4
+ %10 = OpTypeFloat 32
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 4
+ %14 = OpTypeVector %10 2
+ %15 = OpTypePointer Function %14
+ %17 = OpConstant %10 3.4000001
+ %18 = OpConstantComposite %14 %17 %17
+ %19 = OpTypeBool
+ %20 = OpTypeStruct %19
+ %21 = OpTypePointer Function %20
+ %23 = OpConstantTrue %19
+ %24 = OpConstantComposite %20 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ %22 = OpVariable %21 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %18
+ OpStore %22 %24
+ %50 = OpCopyObject %6 %9
+ %51 = OpCopyObject %10 %13
+ %52 = OpCopyObject %10 %17
+ %53 = OpCopyObject %14 %18
+ %54 = OpCopyObject %19 %23
+ %55 = OpCopyObject %20 %24
+ %56 = OpCopyObject %21 %22
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, CopyBooleanConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %8 = OpConstantFalse %6
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_EQ(0, transformation_context.GetFactManager()
+ ->GetIdsForWhichSynonymsAreKnown()
+ .size());
+
+ {
+ TransformationAddSynonym copy_true(
+ 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+ MakeInstructionDescriptor(5, SpvOpReturn, 0));
+ ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
+ copy_true.Apply(context.get(), &transformation_context);
+
+ std::vector<uint32_t> ids_for_which_synonyms_are_known =
+ transformation_context.GetFactManager()
+ ->GetIdsForWhichSynonymsAreKnown();
+ ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
+ ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+ ids_for_which_synonyms_are_known.end(),
+ 7) != ids_for_which_synonyms_are_known.end());
+ ASSERT_EQ(
+ 2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
+ protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(7, {}), descriptor_100));
+ }
+
+ {
+ TransformationAddSynonym copy_false(
+ 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+ MakeInstructionDescriptor(100, SpvOpReturn, 0));
+ ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
+ copy_false.Apply(context.get(), &transformation_context);
+ std::vector<uint32_t> ids_for_which_synonyms_are_known =
+ transformation_context.GetFactManager()
+ ->GetIdsForWhichSynonymsAreKnown();
+ ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
+ ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+ ids_for_which_synonyms_are_known.end(),
+ 8) != ids_for_which_synonyms_are_known.end());
+ ASSERT_EQ(
+ 2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
+ protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(8, {}), descriptor_101));
+ }
+
+ {
+ TransformationAddSynonym copy_false_again(
+ 101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+ MakeInstructionDescriptor(5, SpvOpReturn, 0));
+ ASSERT_TRUE(
+ copy_false_again.IsApplicable(context.get(), transformation_context));
+ copy_false_again.Apply(context.get(), &transformation_context);
+ std::vector<uint32_t> ids_for_which_synonyms_are_known =
+ transformation_context.GetFactManager()
+ ->GetIdsForWhichSynonymsAreKnown();
+ ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
+ ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+ ids_for_which_synonyms_are_known.end(),
+ 101) != ids_for_which_synonyms_are_known.end());
+ ASSERT_EQ(
+ 3,
+ transformation_context.GetFactManager()->GetSynonymsForId(101).size());
+ protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(101, {}), descriptor_102));
+ }
+
+ {
+ TransformationAddSynonym copy_true_again(
+ 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
+ MakeInstructionDescriptor(102, SpvOpReturn, 0));
+ ASSERT_TRUE(
+ copy_true_again.IsApplicable(context.get(), transformation_context));
+ copy_true_again.Apply(context.get(), &transformation_context);
+ std::vector<uint32_t> ids_for_which_synonyms_are_known =
+ transformation_context.GetFactManager()
+ ->GetIdsForWhichSynonymsAreKnown();
+ ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
+ ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
+ ids_for_which_synonyms_are_known.end(),
+ 7) != ids_for_which_synonyms_are_known.end());
+ ASSERT_EQ(
+ 3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
+ protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(7, {}), descriptor_103));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %8 = OpConstantFalse %6
+ %3 = OpTypeFunction %2
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %100 = OpCopyObject %6 %7
+ %101 = OpCopyObject %6 %8
+ %102 = OpCopyObject %6 %101
+ %103 = OpCopyObject %6 %7
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, CheckIllegalCases) {
+ // The following SPIR-V comes from this GLSL, pushed through spirv-opt
+ // and then doctored a bit.
+ //
+ // #version 310 es
+ //
+ // precision highp float;
+ //
+ // struct S {
+ // int a;
+ // float b;
+ // };
+ //
+ // layout(set = 0, binding = 2) uniform block {
+ // S s;
+ // lowp float f;
+ // int ii;
+ // } ubuf;
+ //
+ // layout(location = 0) out vec4 color;
+ //
+ // void main() {
+ // float c = 0.0;
+ // lowp float d = 0.0;
+ // S localS = ubuf.s;
+ // for (int i = 0; i < ubuf.s.a; i++) {
+ // switch (ubuf.ii) {
+ // case 0:
+ // c += 0.1;
+ // d += 0.2;
+ // case 1:
+ // c += 0.1;
+ // if (c > d) {
+ // d += 0.2;
+ // } else {
+ // d += c;
+ // }
+ // break;
+ // default:
+ // i += 1;
+ // localS.b += d;
+ // }
+ // }
+ // color = vec4(c, d, localS.b, 1.0);
+ // }
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %80
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %12 "S"
+ OpMemberName %12 0 "a"
+ OpMemberName %12 1 "b"
+ OpName %15 "S"
+ OpMemberName %15 0 "a"
+ OpMemberName %15 1 "b"
+ OpName %16 "block"
+ OpMemberName %16 0 "s"
+ OpMemberName %16 1 "f"
+ OpMemberName %16 2 "ii"
+ OpName %18 "ubuf"
+ OpName %80 "color"
+ OpMemberDecorate %12 0 RelaxedPrecision
+ OpMemberDecorate %15 0 RelaxedPrecision
+ OpMemberDecorate %15 0 Offset 0
+ OpMemberDecorate %15 1 Offset 4
+ OpMemberDecorate %16 0 Offset 0
+ OpMemberDecorate %16 1 RelaxedPrecision
+ OpMemberDecorate %16 1 Offset 16
+ OpMemberDecorate %16 2 RelaxedPrecision
+ OpMemberDecorate %16 2 Offset 20
+ OpDecorate %16 Block
+ OpDecorate %18 DescriptorSet 0
+ OpDecorate %18 Binding 2
+ OpDecorate %38 RelaxedPrecision
+ OpDecorate %43 RelaxedPrecision
+ OpDecorate %53 RelaxedPrecision
+ OpDecorate %62 RelaxedPrecision
+ OpDecorate %69 RelaxedPrecision
+ OpDecorate %77 RelaxedPrecision
+ OpDecorate %80 Location 0
+ OpDecorate %101 RelaxedPrecision
+ OpDecorate %102 RelaxedPrecision
+ OpDecorate %96 RelaxedPrecision
+ OpDecorate %108 RelaxedPrecision
+ OpDecorate %107 RelaxedPrecision
+ OpDecorate %98 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %9 = OpConstant %6 0
+ %11 = OpTypeInt 32 1
+ %12 = OpTypeStruct %11 %6
+ %15 = OpTypeStruct %11 %6
+ %16 = OpTypeStruct %15 %6 %11
+ %17 = OpTypePointer Uniform %16
+ %18 = OpVariable %17 Uniform
+ %19 = OpConstant %11 0
+ %20 = OpTypePointer Uniform %15
+ %27 = OpConstant %11 1
+ %36 = OpTypePointer Uniform %11
+ %39 = OpTypeBool
+ %41 = OpConstant %11 2
+ %48 = OpConstant %6 0.100000001
+ %51 = OpConstant %6 0.200000003
+ %78 = OpTypeVector %6 4
+ %79 = OpTypePointer Output %78
+ %80 = OpVariable %79 Output
+ %85 = OpConstant %6 1
+ %95 = OpUndef %12
+ %112 = OpTypePointer Uniform %6
+ %113 = OpTypeInt 32 0
+ %114 = OpConstant %113 1
+ %179 = OpTypePointer Function %39
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %180 = OpVariable %179 Function
+ %181 = OpVariable %179 Function
+ %182 = OpVariable %179 Function
+ %21 = OpAccessChain %20 %18 %19
+ %115 = OpAccessChain %112 %21 %114
+ %116 = OpLoad %6 %115
+ %90 = OpCompositeInsert %12 %116 %95 1
+ OpBranch %30
+ %30 = OpLabel
+ %99 = OpPhi %12 %90 %5 %109 %47
+ %98 = OpPhi %6 %9 %5 %107 %47
+ %97 = OpPhi %6 %9 %5 %105 %47
+ %96 = OpPhi %11 %19 %5 %77 %47
+ %37 = OpAccessChain %36 %18 %19 %19
+ %38 = OpLoad %11 %37
+ %40 = OpSLessThan %39 %96 %38
+ OpLoopMerge %32 %47 None
+ OpBranchConditional %40 %31 %32
+ %31 = OpLabel
+ %42 = OpAccessChain %36 %18 %41
+ %43 = OpLoad %11 %42
+ OpSelectionMerge %45 None
+ OpSwitch %43 %46 0 %44 1 %45
+ %46 = OpLabel
+ %69 = OpIAdd %11 %96 %27
+ %72 = OpCompositeExtract %6 %99 1
+ %73 = OpFAdd %6 %72 %98
+ %93 = OpCompositeInsert %12 %73 %99 1
+ OpBranch %47
+ %44 = OpLabel
+ %50 = OpFAdd %6 %97 %48
+ %53 = OpFAdd %6 %98 %51
+ OpBranch %45
+ %45 = OpLabel
+ %101 = OpPhi %6 %98 %31 %53 %44
+ %100 = OpPhi %6 %97 %31 %50 %44
+ %55 = OpFAdd %6 %100 %48
+ %58 = OpFOrdGreaterThan %39 %55 %101
+ OpSelectionMerge %60 None
+ OpBranchConditional %58 %59 %63
+ %59 = OpLabel
+ %62 = OpFAdd %6 %101 %51
+ OpBranch %60
+ %63 = OpLabel
+ %66 = OpFAdd %6 %101 %55
+ OpBranch %60
+ %60 = OpLabel
+ %108 = OpPhi %6 %62 %59 %66 %63
+ OpBranch %47
+ %47 = OpLabel
+ %109 = OpPhi %12 %93 %46 %99 %60
+ %107 = OpPhi %6 %98 %46 %108 %60
+ %105 = OpPhi %6 %97 %46 %55 %60
+ %102 = OpPhi %11 %69 %46 %96 %60
+ %77 = OpIAdd %11 %102 %27
+ OpBranch %30
+ %32 = OpLabel
+ %84 = OpCompositeExtract %6 %99 1
+ %86 = OpCompositeConstruct %78 %97 %98 %84 %85
+ OpStore %80 %86
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Inapplicable because %18 is decorated.
+ ASSERT_FALSE(TransformationAddSynonym(
+ 18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(21, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because %77 is decorated.
+ ASSERT_FALSE(TransformationAddSynonym(
+ 77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(77, SpvOpBranch, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because %80 is decorated.
+ ASSERT_FALSE(TransformationAddSynonym(
+ 80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(77, SpvOpIAdd, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because %84 is not available at the requested point
+ ASSERT_FALSE(TransformationAddSynonym(
+ 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Fine because %84 is available at the requested point
+ ASSERT_TRUE(TransformationAddSynonym(
+ 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because id %9 is already in use
+ ASSERT_FALSE(TransformationAddSynonym(
+ 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9,
+ MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the requested point does not exist
+ ASSERT_FALSE(TransformationAddSynonym(
+ 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(86, SpvOpReturn, 2))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because %9 is not in a function
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(9, SpvOpTypeInt, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the insert point is right before, or inside, a chunk
+ // of OpPhis
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(30, SpvOpPhi, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(99, SpvOpPhi, 1))
+ .IsApplicable(context.get(), transformation_context));
+
+ // OK, because the insert point is just after a chunk of OpPhis.
+ ASSERT_TRUE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(96, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the insert point is right after an OpSelectionMerge
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(58, SpvOpBranchConditional, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // OK, because the insert point is right before the OpSelectionMerge
+ ASSERT_TRUE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the insert point is right after an OpSelectionMerge
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(43, SpvOpSwitch, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // OK, because the insert point is right before the OpSelectionMerge
+ ASSERT_TRUE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the insert point is right after an OpLoopMerge
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(40, SpvOpBranchConditional, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // OK, because the insert point is right before the OpLoopMerge
+ ASSERT_TRUE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because id %300 does not exist
+ ASSERT_FALSE(TransformationAddSynonym(
+ 300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(40, SpvOpLoopMerge, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Inapplicable because the following instruction is OpVariable
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(180, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(181, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(182, SpvOpVariable, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // OK, because this is just past the group of OpVariable instructions.
+ ASSERT_TRUE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200,
+ MakeInstructionDescriptor(182, SpvOpAccessChain, 0))
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, MiscellaneousCopies) {
+ // The following SPIR-V comes from this GLSL:
+ //
+ // #version 310 es
+ //
+ // precision highp float;
+ //
+ // float g;
+ //
+ // vec4 h;
+ //
+ // void main() {
+ // int a;
+ // int b;
+ // b = int(g);
+ // h.x = float(a);
+ // }
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "b"
+ OpName %11 "g"
+ OpName %16 "h"
+ OpName %17 "a"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpTypeFloat 32
+ %10 = OpTypePointer Private %9
+ %11 = OpVariable %10 Private
+ %14 = OpTypeVector %9 4
+ %15 = OpTypePointer Private %14
+ %16 = OpVariable %15 Private
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 0
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %17 = OpVariable %7 Function
+ %12 = OpLoad %9 %11
+ %13 = OpConvertFToS %6 %12
+ OpStore %8 %13
+ %18 = OpLoad %6 %17
+ %19 = OpConvertSToF %9 %18
+ %22 = OpAccessChain %10 %16 %21
+ OpStore %22 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ std::vector<TransformationAddSynonym> transformations = {
+ TransformationAddSynonym(
+ 19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+ MakeInstructionDescriptor(22, SpvOpStore, 0)),
+ TransformationAddSynonym(
+ 22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+ TransformationAddSynonym(
+ 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+ TransformationAddSynonym(
+ 11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+ TransformationAddSynonym(
+ 16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+ TransformationAddSynonym(
+ 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0)),
+ TransformationAddSynonym(
+ 17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106,
+ MakeInstructionDescriptor(22, SpvOpCopyObject, 0))};
+
+ for (auto& transformation : transformations) {
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "b"
+ OpName %11 "g"
+ OpName %16 "h"
+ OpName %17 "a"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpTypeFloat 32
+ %10 = OpTypePointer Private %9
+ %11 = OpVariable %10 Private
+ %14 = OpTypeVector %9 4
+ %15 = OpTypePointer Private %14
+ %16 = OpVariable %15 Private
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 0
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %17 = OpVariable %7 Function
+ %12 = OpLoad %9 %11
+ %13 = OpConvertFToS %6 %12
+ OpStore %8 %13
+ %18 = OpLoad %6 %17
+ %19 = OpConvertSToF %9 %18
+ %22 = OpAccessChain %10 %16 %21
+ %106 = OpCopyObject %7 %17
+ %105 = OpCopyObject %7 %8
+ %104 = OpCopyObject %15 %16
+ %103 = OpCopyObject %10 %11
+ %102 = OpCopyObject %9 %12
+ %101 = OpCopyObject %10 %22
+ %100 = OpCopyObject %9 %19
+ OpStore %22 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpConstantNull %7
+ %9 = OpUndef %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Illegal to copy null.
+ ASSERT_FALSE(TransformationAddSynonym(
+ 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+ MakeInstructionDescriptor(5, SpvOpReturn, 0))
+ .IsApplicable(context.get(), transformation_context));
+
+ // Illegal to copy an OpUndef of pointer type.
+ ASSERT_FALSE(TransformationAddSynonym(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+ MakeInstructionDescriptor(5, SpvOpReturn, 0))
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) {
+ // Checks that if a pointer is known to have an irrelevant value, the same
+ // holds after the pointer is copied.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %9 = OpVariable %7 Function
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
+
+ TransformationAddSynonym transformation1(
+ 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100,
+ MakeInstructionDescriptor(9, SpvOpReturn, 0));
+ TransformationAddSynonym transformation2(
+ 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101,
+ MakeInstructionDescriptor(9, SpvOpReturn, 0));
+ TransformationAddSynonym transformation3(
+ 100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102,
+ MakeInstructionDescriptor(9, SpvOpReturn, 0));
+
+ ASSERT_TRUE(
+ transformation1.IsApplicable(context.get(), transformation_context));
+ transformation1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(
+ transformation2.IsApplicable(context.get(), transformation_context));
+ transformation2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(
+ transformation3.IsApplicable(context.get(), transformation_context));
+ transformation3.Apply(context.get(), &transformation_context);
+
+ ASSERT_TRUE(
+ transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
+ ASSERT_TRUE(
+ transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
+ ASSERT_TRUE(
+ transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
+ ASSERT_FALSE(
+ transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
+ ASSERT_FALSE(
+ transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
+}
+
+TEST(TransformationAddSynonym, DoNotCopyOpSampledImage) {
+ // This checks that we do not try to copy the result id of an OpSampledImage
+ // instruction.
+ std::string shader = R"(
+ OpCapability Shader
+ OpCapability SampledBuffer
+ OpCapability ImageBuffer
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main" %40 %41
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %40 DescriptorSet 0
+ OpDecorate %40 Binding 69
+ OpDecorate %41 DescriptorSet 0
+ OpDecorate %41 Binding 1
+ %54 = OpTypeFloat 32
+ %76 = OpTypeVector %54 4
+ %55 = OpConstant %54 0
+ %56 = OpTypeVector %54 3
+ %94 = OpTypeVector %54 2
+ %112 = OpConstantComposite %94 %55 %55
+ %57 = OpConstantComposite %56 %55 %55 %55
+ %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
+ %114 = OpTypePointer UniformConstant %15
+ %38 = OpTypeSampler
+ %125 = OpTypePointer UniformConstant %38
+ %132 = OpTypeVoid
+ %133 = OpTypeFunction %132
+ %45 = OpTypeSampledImage %15
+ %40 = OpVariable %114 UniformConstant
+ %41 = OpVariable %125 UniformConstant
+ %2 = OpFunction %132 None %133
+ %164 = OpLabel
+ %184 = OpLoad %15 %40
+ %213 = OpLoad %38 %41
+ %216 = OpSampledImage %45 %184 %213
+ %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_FALSE(
+ TransformationAddSynonym(
+ 216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500,
+ MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0))
+ .IsApplicable(context.get(), transformation_context));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
index b663866..df6f382 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_construct_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_composite_construct.h"
+
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -1358,6 +1359,176 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationCompositeConstructTest, AddSynonymsForRelevantIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 3
+ %8 = OpTypePointer Function %7
+ %10 = OpConstant %6 1
+ %11 = OpConstantComposite %7 %10 %10 %10
+ %17 = OpTypeVector %6 4
+ %18 = OpTypePointer Function %17
+ %21 = OpConstant %6 2
+ %32 = OpTypeMatrix %17 3
+ %33 = OpTypePointer Private %32
+ %34 = OpVariable %33 Private
+ %35 = OpTypeMatrix %7 4
+ %36 = OpTypePointer Private %35
+ %37 = OpVariable %36 Private
+ %38 = OpTypeVector %6 2
+ %39 = OpTypeInt 32 0
+ %40 = OpConstant %39 3
+ %41 = OpTypeArray %38 %40
+ %42 = OpTypePointer Private %41
+ %43 = OpVariable %42 Private
+ %100 = OpUndef %7
+ %101 = OpUndef %17
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpVariable %8 Function
+ %12 = OpVariable %8 Function
+ %14 = OpVariable %8 Function
+ %19 = OpVariable %18 Function
+ %26 = OpVariable %18 Function
+ %29 = OpVariable %18 Function
+ OpStore %9 %11
+ %13 = OpLoad %7 %9
+ OpStore %12 %13
+ %15 = OpLoad %7 %12
+ %16 = OpVectorShuffle %7 %15 %15 2 1 0
+ OpStore %14 %16
+ %20 = OpLoad %7 %14
+ %22 = OpCompositeExtract %6 %20 0
+ %23 = OpCompositeExtract %6 %20 1
+ %24 = OpCompositeExtract %6 %20 2
+ %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+ OpStore %19 %25
+ %27 = OpLoad %17 %19
+ %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+ OpStore %26 %28
+ %30 = OpLoad %7 %9
+ %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+ OpStore %29 %31
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ TransformationCompositeConstruct transformation(
+ 32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(200, {0})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+ MakeDataDescriptor(200, {1})));
+}
+
+TEST(TransformationCompositeConstructTest, DontAddSynonymsForIrrelevantIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 3
+ %8 = OpTypePointer Function %7
+ %10 = OpConstant %6 1
+ %11 = OpConstantComposite %7 %10 %10 %10
+ %17 = OpTypeVector %6 4
+ %18 = OpTypePointer Function %17
+ %21 = OpConstant %6 2
+ %32 = OpTypeMatrix %17 3
+ %33 = OpTypePointer Private %32
+ %34 = OpVariable %33 Private
+ %35 = OpTypeMatrix %7 4
+ %36 = OpTypePointer Private %35
+ %37 = OpVariable %36 Private
+ %38 = OpTypeVector %6 2
+ %39 = OpTypeInt 32 0
+ %40 = OpConstant %39 3
+ %41 = OpTypeArray %38 %40
+ %42 = OpTypePointer Private %41
+ %43 = OpVariable %42 Private
+ %100 = OpUndef %7
+ %101 = OpUndef %17
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %9 = OpVariable %8 Function
+ %12 = OpVariable %8 Function
+ %14 = OpVariable %8 Function
+ %19 = OpVariable %18 Function
+ %26 = OpVariable %18 Function
+ %29 = OpVariable %18 Function
+ OpStore %9 %11
+ %13 = OpLoad %7 %9
+ OpStore %12 %13
+ %15 = OpLoad %7 %12
+ %16 = OpVectorShuffle %7 %15 %15 2 1 0
+ OpStore %14 %16
+ %20 = OpLoad %7 %14
+ %22 = OpCompositeExtract %6 %20 0
+ %23 = OpCompositeExtract %6 %20 1
+ %24 = OpCompositeExtract %6 %20 2
+ %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+ OpStore %19 %25
+ %27 = OpLoad %17 %19
+ %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+ OpStore %26 %28
+ %30 = OpLoad %7 %9
+ %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+ OpStore %29 %31
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactIdIsIrrelevant(25);
+
+ TransformationCompositeConstruct transformation(
+ 32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(200, {0})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}),
+ MakeDataDescriptor(200, {1})));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
index a7674a6..5b1a0f1 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_composite_extract_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_composite_extract.h"
+
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -399,6 +400,185 @@
.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationCompositeExtractTest, AddSynonymsForRelevantIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %17 "FunnyPoint"
+ OpMemberName %17 0 "x"
+ OpMemberName %17 1 "y"
+ OpMemberName %17 2 "z"
+ OpName %19 "p"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %12 = OpTypeBool
+ %16 = OpTypeFloat 32
+ %17 = OpTypeStruct %16 %16 %6
+ %81 = OpTypeStruct %17 %16
+ %18 = OpTypePointer Function %17
+ %20 = OpConstant %6 0
+ %23 = OpTypePointer Function %16
+ %26 = OpConstant %6 1
+ %30 = OpConstant %6 2
+ %80 = OpUndef %16
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %19 = OpVariable %18 Function
+ %9 = OpLoad %6 %8
+ %11 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %17 %80 %80 %26
+ %104 = OpCompositeConstruct %81 %100 %80
+ %13 = OpIEqual %12 %9 %11
+ OpSelectionMerge %15 None
+ OpBranchConditional %13 %14 %25
+ %14 = OpLabel
+ %21 = OpLoad %6 %8
+ %22 = OpConvertSToF %16 %21
+ %101 = OpCompositeConstruct %17 %22 %80 %30
+ %24 = OpAccessChain %23 %19 %20
+ OpStore %24 %22
+ OpBranch %15
+ %25 = OpLabel
+ %27 = OpLoad %6 %10
+ %28 = OpConvertSToF %16 %27
+ %102 = OpCompositeConstruct %17 %80 %28 %27
+ %29 = OpAccessChain %23 %19 %26
+ OpStore %29 %28
+ OpBranch %15
+ %15 = OpLabel
+ %31 = OpAccessChain %23 %19 %20
+ %32 = OpLoad %16 %31
+ %33 = OpAccessChain %23 %19 %26
+ %34 = OpLoad %16 %33
+ %103 = OpCompositeConstruct %17 %34 %32 %9
+ %35 = OpFAdd %16 %32 %34
+ %36 = OpConvertFToS %6 %35
+ %37 = OpAccessChain %7 %19 %30
+ OpStore %37 %36
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ TransformationCompositeExtract transformation(
+ MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
+ MakeDataDescriptor(100, {2})));
+}
+
+TEST(TransformationCompositeExtractTest, DontAddSynonymsForIrrelevantIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %17 "FunnyPoint"
+ OpMemberName %17 0 "x"
+ OpMemberName %17 1 "y"
+ OpMemberName %17 2 "z"
+ OpName %19 "p"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %12 = OpTypeBool
+ %16 = OpTypeFloat 32
+ %17 = OpTypeStruct %16 %16 %6
+ %81 = OpTypeStruct %17 %16
+ %18 = OpTypePointer Function %17
+ %20 = OpConstant %6 0
+ %23 = OpTypePointer Function %16
+ %26 = OpConstant %6 1
+ %30 = OpConstant %6 2
+ %80 = OpUndef %16
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %19 = OpVariable %18 Function
+ %9 = OpLoad %6 %8
+ %11 = OpLoad %6 %10
+ %100 = OpCompositeConstruct %17 %80 %80 %26
+ %104 = OpCompositeConstruct %81 %100 %80
+ %13 = OpIEqual %12 %9 %11
+ OpSelectionMerge %15 None
+ OpBranchConditional %13 %14 %25
+ %14 = OpLabel
+ %21 = OpLoad %6 %8
+ %22 = OpConvertSToF %16 %21
+ %101 = OpCompositeConstruct %17 %22 %80 %30
+ %24 = OpAccessChain %23 %19 %20
+ OpStore %24 %22
+ OpBranch %15
+ %25 = OpLabel
+ %27 = OpLoad %6 %10
+ %28 = OpConvertSToF %16 %27
+ %102 = OpCompositeConstruct %17 %80 %28 %27
+ %29 = OpAccessChain %23 %19 %26
+ OpStore %29 %28
+ OpBranch %15
+ %15 = OpLabel
+ %31 = OpAccessChain %23 %19 %20
+ %32 = OpLoad %16 %31
+ %33 = OpAccessChain %23 %19 %26
+ %34 = OpLoad %16 %33
+ %103 = OpCompositeConstruct %17 %34 %32 %9
+ %35 = OpFAdd %16 %32 %34
+ %36 = OpConvertFToS %6 %35
+ %37 = OpAccessChain %7 %19 %30
+ OpStore %37 %36
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactIdIsIrrelevant(100);
+ TransformationCompositeExtract transformation(
+ MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
+ MakeDataDescriptor(100, {2})));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
deleted file mode 100644
index cf9d135..0000000
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_copy_object_test.cpp
+++ /dev/null
@@ -1,778 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <algorithm>
-#include <set>
-#include <unordered_set>
-
-#include "source/fuzz/data_descriptor.h"
-#include "source/fuzz/instruction_descriptor.h"
-#include "source/fuzz/transformation_copy_object.h"
-#include "test/fuzz/fuzz_test_util.h"
-
-namespace spvtools {
-namespace fuzz {
-namespace {
-
-TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
- %2 = OpTypeVoid
- %6 = OpTypeBool
- %7 = OpConstantTrue %6
- %8 = OpConstantFalse %6
- %3 = OpTypeFunction %2
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- ASSERT_EQ(0, transformation_context.GetFactManager()
- ->GetIdsForWhichSynonymsAreKnown()
- .size());
-
- {
- TransformationCopyObject copy_true(
- 7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
- ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context));
- copy_true.Apply(context.get(), &transformation_context);
-
- std::vector<uint32_t> ids_for_which_synonyms_are_known =
- transformation_context.GetFactManager()
- ->GetIdsForWhichSynonymsAreKnown();
- ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
- ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
- ids_for_which_synonyms_are_known.end(),
- 7) != ids_for_which_synonyms_are_known.end());
- ASSERT_EQ(
- 2, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
- protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
- ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
- MakeDataDescriptor(7, {}), descriptor_100));
- }
-
- {
- TransformationCopyObject copy_false(
- 8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
- ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context));
- copy_false.Apply(context.get(), &transformation_context);
- std::vector<uint32_t> ids_for_which_synonyms_are_known =
- transformation_context.GetFactManager()
- ->GetIdsForWhichSynonymsAreKnown();
- ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
- ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
- ids_for_which_synonyms_are_known.end(),
- 8) != ids_for_which_synonyms_are_known.end());
- ASSERT_EQ(
- 2, transformation_context.GetFactManager()->GetSynonymsForId(8).size());
- protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
- ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
- MakeDataDescriptor(8, {}), descriptor_101));
- }
-
- {
- TransformationCopyObject copy_false_again(
- 101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
- ASSERT_TRUE(
- copy_false_again.IsApplicable(context.get(), transformation_context));
- copy_false_again.Apply(context.get(), &transformation_context);
- std::vector<uint32_t> ids_for_which_synonyms_are_known =
- transformation_context.GetFactManager()
- ->GetIdsForWhichSynonymsAreKnown();
- ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
- ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
- ids_for_which_synonyms_are_known.end(),
- 101) != ids_for_which_synonyms_are_known.end());
- ASSERT_EQ(
- 3,
- transformation_context.GetFactManager()->GetSynonymsForId(101).size());
- protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
- ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
- MakeDataDescriptor(101, {}), descriptor_102));
- }
-
- {
- TransformationCopyObject copy_true_again(
- 7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
- ASSERT_TRUE(
- copy_true_again.IsApplicable(context.get(), transformation_context));
- copy_true_again.Apply(context.get(), &transformation_context);
- std::vector<uint32_t> ids_for_which_synonyms_are_known =
- transformation_context.GetFactManager()
- ->GetIdsForWhichSynonymsAreKnown();
- ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
- ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
- ids_for_which_synonyms_are_known.end(),
- 7) != ids_for_which_synonyms_are_known.end());
- ASSERT_EQ(
- 3, transformation_context.GetFactManager()->GetSynonymsForId(7).size());
- protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
- ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
- MakeDataDescriptor(7, {}), descriptor_103));
- }
-
- std::string after_transformation = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
- %2 = OpTypeVoid
- %6 = OpTypeBool
- %7 = OpConstantTrue %6
- %8 = OpConstantFalse %6
- %3 = OpTypeFunction %2
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %100 = OpCopyObject %6 %7
- %101 = OpCopyObject %6 %8
- %102 = OpCopyObject %6 %101
- %103 = OpCopyObject %6 %7
- OpReturn
- OpFunctionEnd
- )";
-
- ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
-}
-
-TEST(TransformationCopyObjectTest, CheckIllegalCases) {
- // The following SPIR-V comes from this GLSL, pushed through spirv-opt
- // and then doctored a bit.
- //
- // #version 310 es
- //
- // precision highp float;
- //
- // struct S {
- // int a;
- // float b;
- // };
- //
- // layout(set = 0, binding = 2) uniform block {
- // S s;
- // lowp float f;
- // int ii;
- // } ubuf;
- //
- // layout(location = 0) out vec4 color;
- //
- // void main() {
- // float c = 0.0;
- // lowp float d = 0.0;
- // S localS = ubuf.s;
- // for (int i = 0; i < ubuf.s.a; i++) {
- // switch (ubuf.ii) {
- // case 0:
- // c += 0.1;
- // d += 0.2;
- // case 1:
- // c += 0.1;
- // if (c > d) {
- // d += 0.2;
- // } else {
- // d += c;
- // }
- // break;
- // default:
- // i += 1;
- // localS.b += d;
- // }
- // }
- // color = vec4(c, d, localS.b, 1.0);
- // }
-
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main" %80
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
- OpName %12 "S"
- OpMemberName %12 0 "a"
- OpMemberName %12 1 "b"
- OpName %15 "S"
- OpMemberName %15 0 "a"
- OpMemberName %15 1 "b"
- OpName %16 "block"
- OpMemberName %16 0 "s"
- OpMemberName %16 1 "f"
- OpMemberName %16 2 "ii"
- OpName %18 "ubuf"
- OpName %80 "color"
- OpMemberDecorate %12 0 RelaxedPrecision
- OpMemberDecorate %15 0 RelaxedPrecision
- OpMemberDecorate %15 0 Offset 0
- OpMemberDecorate %15 1 Offset 4
- OpMemberDecorate %16 0 Offset 0
- OpMemberDecorate %16 1 RelaxedPrecision
- OpMemberDecorate %16 1 Offset 16
- OpMemberDecorate %16 2 RelaxedPrecision
- OpMemberDecorate %16 2 Offset 20
- OpDecorate %16 Block
- OpDecorate %18 DescriptorSet 0
- OpDecorate %18 Binding 2
- OpDecorate %38 RelaxedPrecision
- OpDecorate %43 RelaxedPrecision
- OpDecorate %53 RelaxedPrecision
- OpDecorate %62 RelaxedPrecision
- OpDecorate %69 RelaxedPrecision
- OpDecorate %77 RelaxedPrecision
- OpDecorate %80 Location 0
- OpDecorate %101 RelaxedPrecision
- OpDecorate %102 RelaxedPrecision
- OpDecorate %96 RelaxedPrecision
- OpDecorate %108 RelaxedPrecision
- OpDecorate %107 RelaxedPrecision
- OpDecorate %98 RelaxedPrecision
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeFloat 32
- %9 = OpConstant %6 0
- %11 = OpTypeInt 32 1
- %12 = OpTypeStruct %11 %6
- %15 = OpTypeStruct %11 %6
- %16 = OpTypeStruct %15 %6 %11
- %17 = OpTypePointer Uniform %16
- %18 = OpVariable %17 Uniform
- %19 = OpConstant %11 0
- %20 = OpTypePointer Uniform %15
- %27 = OpConstant %11 1
- %36 = OpTypePointer Uniform %11
- %39 = OpTypeBool
- %41 = OpConstant %11 2
- %48 = OpConstant %6 0.100000001
- %51 = OpConstant %6 0.200000003
- %78 = OpTypeVector %6 4
- %79 = OpTypePointer Output %78
- %80 = OpVariable %79 Output
- %85 = OpConstant %6 1
- %95 = OpUndef %12
- %112 = OpTypePointer Uniform %6
- %113 = OpTypeInt 32 0
- %114 = OpConstant %113 1
- %179 = OpTypePointer Function %39
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %180 = OpVariable %179 Function
- %181 = OpVariable %179 Function
- %182 = OpVariable %179 Function
- %21 = OpAccessChain %20 %18 %19
- %115 = OpAccessChain %112 %21 %114
- %116 = OpLoad %6 %115
- %90 = OpCompositeInsert %12 %116 %95 1
- OpBranch %30
- %30 = OpLabel
- %99 = OpPhi %12 %90 %5 %109 %47
- %98 = OpPhi %6 %9 %5 %107 %47
- %97 = OpPhi %6 %9 %5 %105 %47
- %96 = OpPhi %11 %19 %5 %77 %47
- %37 = OpAccessChain %36 %18 %19 %19
- %38 = OpLoad %11 %37
- %40 = OpSLessThan %39 %96 %38
- OpLoopMerge %32 %47 None
- OpBranchConditional %40 %31 %32
- %31 = OpLabel
- %42 = OpAccessChain %36 %18 %41
- %43 = OpLoad %11 %42
- OpSelectionMerge %45 None
- OpSwitch %43 %46 0 %44 1 %45
- %46 = OpLabel
- %69 = OpIAdd %11 %96 %27
- %72 = OpCompositeExtract %6 %99 1
- %73 = OpFAdd %6 %72 %98
- %93 = OpCompositeInsert %12 %73 %99 1
- OpBranch %47
- %44 = OpLabel
- %50 = OpFAdd %6 %97 %48
- %53 = OpFAdd %6 %98 %51
- OpBranch %45
- %45 = OpLabel
- %101 = OpPhi %6 %98 %31 %53 %44
- %100 = OpPhi %6 %97 %31 %50 %44
- %55 = OpFAdd %6 %100 %48
- %58 = OpFOrdGreaterThan %39 %55 %101
- OpSelectionMerge %60 None
- OpBranchConditional %58 %59 %63
- %59 = OpLabel
- %62 = OpFAdd %6 %101 %51
- OpBranch %60
- %63 = OpLabel
- %66 = OpFAdd %6 %101 %55
- OpBranch %60
- %60 = OpLabel
- %108 = OpPhi %6 %62 %59 %66 %63
- OpBranch %47
- %47 = OpLabel
- %109 = OpPhi %12 %93 %46 %99 %60
- %107 = OpPhi %6 %98 %46 %108 %60
- %105 = OpPhi %6 %97 %46 %55 %60
- %102 = OpPhi %11 %69 %46 %96 %60
- %77 = OpIAdd %11 %102 %27
- OpBranch %30
- %32 = OpLabel
- %84 = OpCompositeExtract %6 %99 1
- %86 = OpCompositeConstruct %78 %97 %98 %84 %85
- OpStore %80 %86
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- // Inapplicable because %18 is decorated.
- ASSERT_FALSE(TransformationCopyObject(
- 18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because %77 is decorated.
- ASSERT_FALSE(TransformationCopyObject(
- 77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because %80 is decorated.
- ASSERT_FALSE(TransformationCopyObject(
- 80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because %84 is not available at the requested point
- ASSERT_FALSE(
- TransformationCopyObject(
- 84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Fine because %84 is available at the requested point
- ASSERT_TRUE(
- TransformationCopyObject(
- 84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because id %9 is already in use
- ASSERT_FALSE(
- TransformationCopyObject(
- 84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the requested point does not exist
- ASSERT_FALSE(TransformationCopyObject(
- 84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because %9 is not in a function
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the insert point is right before, or inside, a chunk
- // of OpPhis
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // OK, because the insert point is just after a chunk of OpPhis.
- ASSERT_TRUE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the insert point is right after an OpSelectionMerge
- ASSERT_FALSE(
- TransformationCopyObject(
- 9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // OK, because the insert point is right before the OpSelectionMerge
- ASSERT_TRUE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the insert point is right after an OpSelectionMerge
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // OK, because the insert point is right before the OpSelectionMerge
- ASSERT_TRUE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the insert point is right after an OpLoopMerge
- ASSERT_FALSE(
- TransformationCopyObject(
- 9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // OK, because the insert point is right before the OpLoopMerge
- ASSERT_TRUE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because id %300 does not exist
- ASSERT_FALSE(TransformationCopyObject(
- 300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // Inapplicable because the following instruction is OpVariable
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-
- // OK, because this is just past the group of OpVariable instructions.
- ASSERT_TRUE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
- .IsApplicable(context.get(), transformation_context));
-}
-
-TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
- // The following SPIR-V comes from this GLSL:
- //
- // #version 310 es
- //
- // precision highp float;
- //
- // float g;
- //
- // vec4 h;
- //
- // void main() {
- // int a;
- // int b;
- // b = int(g);
- // h.x = float(a);
- // }
-
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
- OpName %8 "b"
- OpName %11 "g"
- OpName %16 "h"
- OpName %17 "a"
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeInt 32 1
- %7 = OpTypePointer Function %6
- %9 = OpTypeFloat 32
- %10 = OpTypePointer Private %9
- %11 = OpVariable %10 Private
- %14 = OpTypeVector %9 4
- %15 = OpTypePointer Private %14
- %16 = OpVariable %15 Private
- %20 = OpTypeInt 32 0
- %21 = OpConstant %20 0
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %8 = OpVariable %7 Function
- %17 = OpVariable %7 Function
- %12 = OpLoad %9 %11
- %13 = OpConvertFToS %6 %12
- OpStore %8 %13
- %18 = OpLoad %6 %17
- %19 = OpConvertSToF %9 %18
- %22 = OpAccessChain %10 %16 %21
- OpStore %22 %19
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- std::vector<TransformationCopyObject> transformations = {
- TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
- 100),
- TransformationCopyObject(
- 22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
- TransformationCopyObject(
- 12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
- TransformationCopyObject(
- 11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
- TransformationCopyObject(
- 16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
- TransformationCopyObject(
- 8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
- TransformationCopyObject(
- 17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
-
- for (auto& transformation : transformations) {
- ASSERT_TRUE(
- transformation.IsApplicable(context.get(), transformation_context));
- transformation.Apply(context.get(), &transformation_context);
- }
-
- ASSERT_TRUE(IsValid(env, context.get()));
-
- std::string after_transformation = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
- OpName %8 "b"
- OpName %11 "g"
- OpName %16 "h"
- OpName %17 "a"
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeInt 32 1
- %7 = OpTypePointer Function %6
- %9 = OpTypeFloat 32
- %10 = OpTypePointer Private %9
- %11 = OpVariable %10 Private
- %14 = OpTypeVector %9 4
- %15 = OpTypePointer Private %14
- %16 = OpVariable %15 Private
- %20 = OpTypeInt 32 0
- %21 = OpConstant %20 0
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %8 = OpVariable %7 Function
- %17 = OpVariable %7 Function
- %12 = OpLoad %9 %11
- %13 = OpConvertFToS %6 %12
- OpStore %8 %13
- %18 = OpLoad %6 %17
- %19 = OpConvertSToF %9 %18
- %22 = OpAccessChain %10 %16 %21
- %106 = OpCopyObject %7 %17
- %105 = OpCopyObject %7 %8
- %104 = OpCopyObject %15 %16
- %103 = OpCopyObject %10 %11
- %102 = OpCopyObject %9 %12
- %101 = OpCopyObject %10 %22
- %100 = OpCopyObject %9 %19
- OpStore %22 %19
- OpReturn
- OpFunctionEnd
- )";
-
- ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
-}
-
-TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) {
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeInt 32 1
- %7 = OpTypePointer Function %6
- %8 = OpConstantNull %7
- %9 = OpUndef %7
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- // Illegal to copy null.
- ASSERT_FALSE(TransformationCopyObject(
- 8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
- .IsApplicable(context.get(), transformation_context));
-
- // Illegal to copy an OpUndef of pointer type.
- ASSERT_FALSE(TransformationCopyObject(
- 9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
- .IsApplicable(context.get(), transformation_context));
-}
-
-TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) {
- // Checks that if a pointer is known to have an irrelevant value, the same
- // holds after the pointer is copied.
-
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- %2 = OpTypeVoid
- %3 = OpTypeFunction %2
- %6 = OpTypeInt 32 1
- %7 = OpTypePointer Function %6
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- %8 = OpVariable %7 Function
- %9 = OpVariable %7 Function
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- ASSERT_TRUE(IsValid(env, context.get()));
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8);
-
- TransformationCopyObject transformation1(
- 8, MakeInstructionDescriptor(9, SpvOpReturn, 0), 100);
- TransformationCopyObject transformation2(
- 9, MakeInstructionDescriptor(9, SpvOpReturn, 0), 101);
- TransformationCopyObject transformation3(
- 100, MakeInstructionDescriptor(9, SpvOpReturn, 0), 102);
-
- ASSERT_TRUE(
- transformation1.IsApplicable(context.get(), transformation_context));
- transformation1.Apply(context.get(), &transformation_context);
- ASSERT_TRUE(
- transformation2.IsApplicable(context.get(), transformation_context));
- transformation2.Apply(context.get(), &transformation_context);
- ASSERT_TRUE(
- transformation3.IsApplicable(context.get(), transformation_context));
- transformation3.Apply(context.get(), &transformation_context);
-
- ASSERT_TRUE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8));
- ASSERT_TRUE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100));
- ASSERT_TRUE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102));
- ASSERT_FALSE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9));
- ASSERT_FALSE(
- transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101));
-}
-
-TEST(TransformationCopyObject, DoNotCopyOpSampledImage) {
- // This checks that we do not try to copy the result id of an OpSampledImage
- // instruction.
- std::string shader = R"(
- OpCapability Shader
- OpCapability SampledBuffer
- OpCapability ImageBuffer
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "main" %40 %41
- OpExecutionMode %2 OriginUpperLeft
- OpSource GLSL 450
- OpDecorate %40 DescriptorSet 0
- OpDecorate %40 Binding 69
- OpDecorate %41 DescriptorSet 0
- OpDecorate %41 Binding 1
- %54 = OpTypeFloat 32
- %76 = OpTypeVector %54 4
- %55 = OpConstant %54 0
- %56 = OpTypeVector %54 3
- %94 = OpTypeVector %54 2
- %112 = OpConstantComposite %94 %55 %55
- %57 = OpConstantComposite %56 %55 %55 %55
- %15 = OpTypeImage %54 2D 2 0 0 1 Unknown
- %114 = OpTypePointer UniformConstant %15
- %38 = OpTypeSampler
- %125 = OpTypePointer UniformConstant %38
- %132 = OpTypeVoid
- %133 = OpTypeFunction %132
- %45 = OpTypeSampledImage %15
- %40 = OpVariable %114 UniformConstant
- %41 = OpVariable %125 UniformConstant
- %2 = OpFunction %132 None %133
- %164 = OpLabel
- %184 = OpLoad %15 %40
- %213 = OpLoad %38 %41
- %216 = OpSampledImage %45 %184 %213
- %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55
- OpReturn
- OpFunctionEnd
- )";
-
- const auto env = SPV_ENV_UNIVERSAL_1_3;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
-
- FactManager fact_manager;
- spvtools::ValidatorOptions validator_options;
- TransformationContext transformation_context(&fact_manager,
- validator_options);
-
- ASSERT_FALSE(
- TransformationCopyObject(
- 216, MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0),
- 500)
- .IsApplicable(context.get(), transformation_context));
-}
-
-} // namespace
-} // namespace fuzz
-} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
index 1e8aa7e..132e446 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_equation_instruction.h"
+
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -486,6 +487,685 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationEquationInstructionTest, Bitcast) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %6 2
+ %10 = OpTypeVector %7 2
+ %11 = OpTypeVector %8 2
+ %21 = OpTypeBool
+ %22 = OpTypeVector %21 2
+ %15 = OpConstant %6 24
+ %16 = OpConstant %7 24
+ %17 = OpConstant %8 24
+ %18 = OpConstantComposite %9 %15 %15
+ %19 = OpConstantComposite %10 %16 %16
+ %20 = OpConstantComposite %11 %17 %17
+ %23 = OpConstantTrue %21
+ %24 = OpConstantComposite %22 %23 %23
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Too many operands.
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpBitcast, {15, 16},
+ insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Too few operands.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Operand's id is invalid.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {50}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Operand's type is invalid
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {13}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Operand must be a scalar or a vector of numerical type.
+#ifndef NDEBUG
+ ASSERT_DEATH(
+ TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
+ .IsApplicable(context.get(), transformation_context),
+ "Operand is not a scalar or a vector of numerical type");
+ ASSERT_DEATH(
+ TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
+ .IsApplicable(context.get(), transformation_context),
+ "Only vectors of numerical components are supported");
+#else
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+#endif
+
+ for (uint32_t operand_id = 15, fresh_id = 50; operand_id <= 20;
+ ++operand_id, ++fresh_id) {
+ TransformationEquationInstruction transformation(
+ fresh_id, SpvOpBitcast, {operand_id}, insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %6 2
+ %10 = OpTypeVector %7 2
+ %11 = OpTypeVector %8 2
+ %21 = OpTypeBool
+ %22 = OpTypeVector %21 2
+ %15 = OpConstant %6 24
+ %16 = OpConstant %7 24
+ %17 = OpConstant %8 24
+ %18 = OpConstantComposite %9 %15 %15
+ %19 = OpConstantComposite %10 %16 %16
+ %20 = OpConstantComposite %11 %17 %17
+ %23 = OpConstantTrue %21
+ %24 = OpConstantComposite %22 %23 %23
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %8 %15
+ %51 = OpBitcast %8 %16
+ %52 = OpBitcast %6 %17
+ %53 = OpBitcast %11 %18
+ %54 = OpBitcast %11 %19
+ %55 = OpBitcast %9 %20
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest,
+ BitcastResultTypeFloatDoesNotExist) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %9 = OpTypeVector %6 2
+ %10 = OpTypeVector %7 2
+ %15 = OpConstant %6 24
+ %16 = OpConstant %7 24
+ %18 = OpConstantComposite %9 %15 %15
+ %19 = OpConstantComposite %10 %16 %16
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Scalar floating-point type does not exist.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {15}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {16}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Vector of floating-point components does not exist.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {18}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {19}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist1) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %8 = OpTypeFloat 32
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Scalar integral type does not exist.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {17}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Vector of integral components does not exist.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(50, SpvOpBitcast, {20}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ %51 = OpBitcast %9 %20
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist3) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ %51 = OpBitcast %9 %20
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist4) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %8 = OpTypeFloat 32
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_FALSE(
+ TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %8 = OpTypeFloat 32
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist5) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_FALSE(
+ TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before)
+ .IsApplicable(context.get(), transformation_context));
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist6) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %5 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %5 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ %51 = OpBitcast %9 %20
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist7) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpBitcast, {17},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(51, SpvOpBitcast, {20},
+ insert_before);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 1
+ %5 = OpTypeInt 32 0
+ %8 = OpTypeFloat 32
+ %9 = OpTypeVector %4 2
+ %11 = OpTypeVector %8 2
+ %17 = OpConstant %8 24
+ %20 = OpConstantComposite %11 %17 %17
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpBitcast %4 %17
+ %51 = OpBitcast %9 %20
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
TEST(TransformationEquationInstructionTest, Miscellaneous1) {
std::string shader = R"(
OpCapability Shader
@@ -626,6 +1306,286 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationEquationInstructionTest, ConversionInstructions) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeFloat 32
+ %7 = OpTypeVector %6 3
+ %8 = OpTypeVector %4 3
+ %9 = OpTypeVector %5 3
+ %10 = OpConstant %6 12
+ %20 = OpConstant %6 12
+ %11 = OpConstant %4 12
+ %21 = OpConstant %4 12
+ %14 = OpConstant %5 12
+ %15 = OpConstantComposite %7 %10 %10 %10
+ %18 = OpConstantComposite %7 %10 %10 %10
+ %16 = OpConstantComposite %8 %11 %11 %11
+ %19 = OpConstantComposite %8 %11 %11 %11
+ %17 = OpConstantComposite %9 %14 %14 %14
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Too few instruction operands.
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Too many instruction operands.
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {15, 16},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Operand has no type id.
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {7},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpConvertSToF and OpConvertUToF require an operand to have scalar or vector
+ // of integral components type.
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {17},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {14},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {17},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {14},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+
+ {
+ TransformationEquationInstruction transformation(50, SpvOpConvertSToF, {15},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(51, SpvOpConvertSToF, {10},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(52, SpvOpConvertUToF, {16},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(53, SpvOpConvertUToF, {11},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(58, SpvOpConvertSToF, {18},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(59, SpvOpConvertUToF, {19},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(60, SpvOpConvertSToF, {20},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationEquationInstruction transformation(61, SpvOpConvertUToF, {21},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformations = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeFloat 32
+ %7 = OpTypeVector %6 3
+ %8 = OpTypeVector %4 3
+ %9 = OpTypeVector %5 3
+ %10 = OpConstant %6 12
+ %20 = OpConstant %6 12
+ %11 = OpConstant %4 12
+ %21 = OpConstant %4 12
+ %14 = OpConstant %5 12
+ %15 = OpConstantComposite %7 %10 %10 %10
+ %18 = OpConstantComposite %7 %10 %10 %10
+ %16 = OpConstantComposite %8 %11 %11 %11
+ %19 = OpConstantComposite %8 %11 %11 %11
+ %17 = OpConstantComposite %9 %14 %14 %14
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %50 = OpConvertSToF %9 %15
+ %51 = OpConvertSToF %5 %10
+ %52 = OpConvertUToF %9 %16
+ %53 = OpConvertUToF %5 %11
+ %58 = OpConvertSToF %9 %18
+ %59 = OpConvertUToF %9 %19
+ %60 = OpConvertSToF %5 %20
+ %61 = OpConvertUToF %5 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, FloatResultTypeDoesNotExist) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeVector %6 3
+ %9 = OpTypeVector %7 3
+ %10 = OpConstant %6 24
+ %11 = OpConstant %7 25
+ %14 = OpConstantComposite %8 %10 %10 %10
+ %15 = OpConstantComposite %9 %11 %11 %11
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Scalar float type doesn't exist.
+ ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {10},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {11},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Vector float type doesn't exist.
+ ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {14},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {15},
+ return_instruction)
+ .IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationEquationInstructionTest, HandlesIrrelevantIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %30 = OpTypeVector %6 3
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %31 = OpConstantComposite %30 %15 %16 %15
+ %33 = OpTypeBool
+ %32 = OpConstantTrue %33
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Applicable.
+ TransformationEquationInstruction transformation(14, SpvOpIAdd, {15, 16},
+ return_instruction);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Handles irrelevant ids.
+ fact_manager.AddFactIdIsIrrelevant(16);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ fact_manager.AddFactIdIsIrrelevant(15);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp
new file mode 100644
index 0000000..0468469
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_invert_comparison_operator_test.cpp
@@ -0,0 +1,135 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_invert_comparison_operator.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationInvertComparisonOperatorTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeBool
+ %9 = OpConstant %6 3
+ %10 = OpConstant %6 4
+ %11 = OpConstant %7 3
+ %12 = OpConstant %7 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %13 = OpSLessThan %8 %9 %10
+ %14 = OpSLessThanEqual %8 %9 %10
+ %15 = OpSGreaterThan %8 %9 %10
+ %16 = OpSGreaterThanEqual %8 %9 %10
+ %17 = OpULessThan %8 %11 %12
+ %18 = OpULessThanEqual %8 %11 %12
+ %19 = OpUGreaterThan %8 %11 %12
+ %20 = OpUGreaterThanEqual %8 %11 %12
+ %21 = OpIEqual %8 %9 %10
+ %22 = OpINotEqual %8 %9 %10
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Operator id is not valid.
+ ASSERT_FALSE(TransformationInvertComparisonOperator(23, 23).IsApplicable(
+ context.get(), transformation_context));
+
+ // Operator instruction is not supported.
+ ASSERT_FALSE(TransformationInvertComparisonOperator(5, 23).IsApplicable(
+ context.get(), transformation_context));
+
+ // Fresh id is not fresh.
+ ASSERT_FALSE(TransformationInvertComparisonOperator(13, 22).IsApplicable(
+ context.get(), transformation_context));
+
+ for (uint32_t fresh_id = 23, operator_id = 13; operator_id <= 22;
+ ++fresh_id, ++operator_id) {
+ TransformationInvertComparisonOperator transformation(operator_id,
+ fresh_id);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ std::string expected = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeBool
+ %9 = OpConstant %6 3
+ %10 = OpConstant %6 4
+ %11 = OpConstant %7 3
+ %12 = OpConstant %7 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %23 = OpSGreaterThanEqual %8 %9 %10
+ %13 = OpLogicalNot %8 %23
+ %24 = OpSGreaterThan %8 %9 %10
+ %14 = OpLogicalNot %8 %24
+ %25 = OpSLessThanEqual %8 %9 %10
+ %15 = OpLogicalNot %8 %25
+ %26 = OpSLessThan %8 %9 %10
+ %16 = OpLogicalNot %8 %26
+ %27 = OpUGreaterThanEqual %8 %11 %12
+ %17 = OpLogicalNot %8 %27
+ %28 = OpUGreaterThan %8 %11 %12
+ %18 = OpLogicalNot %8 %28
+ %29 = OpULessThanEqual %8 %11 %12
+ %19 = OpLogicalNot %8 %29
+ %30 = OpULessThan %8 %11 %12
+ %20 = OpLogicalNot %8 %30
+ %31 = OpINotEqual %8 %9 %10
+ %21 = OpLogicalNot %8 %31
+ %32 = OpIEqual %8 %9 %10
+ %22 = OpLogicalNot %8 %32
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
\ No newline at end of file
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
index a4a7c00..4046f79 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_permute_function_parameters.h"
+
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -73,7 +74,6 @@
%18 = OpTypeFunction %8 %7 %15 %17
%24 = OpTypeBool
%25 = OpTypeFunction %24 %15 %7
- %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
%31 = OpConstant %6 255
%33 = OpConstant %6 0
%34 = OpConstant %6 1
@@ -90,6 +90,11 @@
%84 = OpConstant %14 5
%90 = OpConstant %6 3
%98 = OpConstant %6 4
+ %206 = OpTypeFunction %2 %14 %16
+ %223 = OpTypeFunction %2 %6 %8
+ %224 = OpTypeFunction %2 %8 %6
+ %233 = OpTypeFunction %2 %42 %24
+ %234 = OpTypeFunction %2 %24 %42
%4 = OpFunction %2 None %3
%5 = OpLabel
%66 = OpVariable %15 Function
@@ -148,6 +153,8 @@
%70 = OpLabel
OpReturn
OpFunctionEnd
+
+ ; adjust type of the function in-place
%12 = OpFunction %8 None %9
%10 = OpFunctionParameter %7
%11 = OpFunctionParameter %7
@@ -192,6 +199,53 @@
OpReturnValue %61
OpFunctionEnd
+ ; create a new function type
+ %200 = OpFunction %2 None %206
+ %207 = OpFunctionParameter %14
+ %208 = OpFunctionParameter %16
+ %202 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %203 = OpFunction %2 None %206
+ %209 = OpFunctionParameter %14
+ %210 = OpFunctionParameter %16
+ %205 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; reuse an existing function type
+ %211 = OpFunction %2 None %223
+ %212 = OpFunctionParameter %6
+ %213 = OpFunctionParameter %8
+ %214 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %215 = OpFunction %2 None %224
+ %216 = OpFunctionParameter %8
+ %217 = OpFunctionParameter %6
+ %218 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %219 = OpFunction %2 None %224
+ %220 = OpFunctionParameter %8
+ %221 = OpFunctionParameter %6
+ %222 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; don't adjust the type of the function if it creates a duplicate
+ %225 = OpFunction %2 None %233
+ %226 = OpFunctionParameter %42
+ %227 = OpFunctionParameter %24
+ %228 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %229 = OpFunction %2 None %234
+ %230 = OpFunctionParameter %24
+ %231 = OpFunctionParameter %42
+ %232 = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
@@ -205,49 +259,67 @@
validator_options);
// Can't permute main function
- ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
- context.get(), transformation_context));
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 105, {})
+ .IsApplicable(context.get(), transformation_context));
// Can't permute invalid instruction
- ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 105, {})
.IsApplicable(context.get(), transformation_context));
// Permutation has too many values
- ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {2, 1, 0, 3})
.IsApplicable(context.get(), transformation_context));
// Permutation has too few values
- ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {0, 1})
.IsApplicable(context.get(), transformation_context));
- // Permutation has invalid values
- ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
+ // Permutation has invalid values 1
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {3, 1, 0})
.IsApplicable(context.get(), transformation_context));
- // Type id is not an OpTypeFunction instruction
+#ifndef NDEBUG
+ // Permutation has invalid values 2
+ ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 105, {2, 2, 1})
+ .IsApplicable(context.get(), transformation_context),
+ "Permutation has duplicates");
+#endif
+
+ // Result id for new function type is not fresh.
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
.IsApplicable(context.get(), transformation_context));
- // Type id has incorrect number of operands
- ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
- .IsApplicable(context.get(), transformation_context));
-
- // OpTypeFunction has operands out of order
- ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
- .IsApplicable(context.get(), transformation_context));
-
// Successful transformations
{
- // Function has two operands of the same type:
- // initial OpTypeFunction should be enough
- TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
+ TransformationPermuteFunctionParameters transformation(12, 105, {1, 0});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
}
{
- TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
+ TransformationPermuteFunctionParameters transformation(28, 106, {1, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+ {
+ TransformationPermuteFunctionParameters transformation(200, 107, {1, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+ {
+ TransformationPermuteFunctionParameters transformation(219, 108, {1, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+ {
+ TransformationPermuteFunctionParameters transformation(229, 109, {1, 0});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
@@ -306,8 +378,6 @@
%17 = OpTypePointer Function %16
%18 = OpTypeFunction %8 %7 %15 %17
%24 = OpTypeBool
- %25 = OpTypeFunction %24 %15 %7
- %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
%31 = OpConstant %6 255
%33 = OpConstant %6 0
%34 = OpConstant %6 1
@@ -324,6 +394,12 @@
%84 = OpConstant %14 5
%90 = OpConstant %6 3
%98 = OpConstant %6 4
+ %206 = OpTypeFunction %2 %14 %16
+ %223 = OpTypeFunction %2 %6 %8
+ %224 = OpTypeFunction %2 %8 %6
+ %233 = OpTypeFunction %2 %42 %24
+ %25 = OpTypeFunction %24 %7 %15
+ %107 = OpTypeFunction %2 %16 %14
%4 = OpFunction %2 None %3
%5 = OpLabel
%66 = OpVariable %15 Function
@@ -415,7 +491,7 @@
%55 = OpFunctionCall %8 %12 %54 %53
OpReturnValue %55
OpFunctionEnd
- %28 = OpFunction %24 None %105
+ %28 = OpFunction %24 None %25
%27 = OpFunctionParameter %7
%26 = OpFunctionParameter %15
%29 = OpLabel
@@ -425,6 +501,48 @@
%61 = OpFOrdLessThan %24 %59 %60
OpReturnValue %61
OpFunctionEnd
+ %200 = OpFunction %2 None %107
+ %208 = OpFunctionParameter %16
+ %207 = OpFunctionParameter %14
+ %202 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %203 = OpFunction %2 None %206
+ %209 = OpFunctionParameter %14
+ %210 = OpFunctionParameter %16
+ %205 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %211 = OpFunction %2 None %223
+ %212 = OpFunctionParameter %6
+ %213 = OpFunctionParameter %8
+ %214 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %215 = OpFunction %2 None %224
+ %216 = OpFunctionParameter %8
+ %217 = OpFunctionParameter %6
+ %218 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %219 = OpFunction %2 None %223
+ %221 = OpFunctionParameter %6
+ %220 = OpFunctionParameter %8
+ %222 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %225 = OpFunction %2 None %233
+ %226 = OpFunctionParameter %42
+ %227 = OpFunctionParameter %24
+ %228 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %229 = OpFunction %2 None %233
+ %231 = OpFunctionParameter %42
+ %230 = OpFunctionParameter %24
+ %232 = OpLabel
+ OpReturn
+ OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp
new file mode 100644
index 0000000..c0a428a
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_phi_operands_test.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_permute_phi_operands.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPermutePhiOperandsTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Result id is invalid.
+ ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Result id is not of an OpPhi instruction.
+ ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Result id is not of an OpPhi instruction.
+ ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Permutation has invalid size.
+ ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2})
+ .IsApplicable(context.get(), transformation_context));
+
+#ifndef NDEBUG
+ // Permutation has duplicates.
+ ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0})
+ .IsApplicable(context.get(), transformation_context),
+ "Permutation has duplicates");
+#endif
+
+ // Permutation's values are not in range.
+ ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2})
+ .IsApplicable(context.get(), transformation_context));
+
+ TransformationPermutePhiOperands transformation(25, {1, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %24 %21 %20 %16
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_push_id_through_variable_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_push_id_through_variable_test.cpp
new file mode 100644
index 0000000..eac7820
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -0,0 +1,707 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_push_id_through_variable.h"
+
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPushIdThroughVariableTest, IsApplicable) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %92 %52 %53
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %92 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeFloat 32
+ %8 = OpTypeStruct %6 %7
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %6 %9
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 2
+ %23 = OpConstant %6 1
+ %24 = OpConstant %7 1
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Private %7
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %60 = OpConstantNull %50
+ %61 = OpUndef %51
+ %52 = OpVariable %50 Private
+ %53 = OpVariable %51 Private
+ %80 = OpConstantComposite %8 %21 %24
+ %90 = OpTypeVector %7 4
+ %91 = OpTypePointer Input %90
+ %92 = OpVariable %91 Input
+ %93 = OpConstantComposite %90 %24 %24 %24 %24
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %20 = OpVariable %9 Function
+ %27 = OpVariable %9 Function
+ %22 = OpAccessChain %15 %20 %14
+ %44 = OpCopyObject %9 %20
+ %26 = OpAccessChain %25 %20 %23
+ %29 = OpFunctionCall %6 %12 %27
+ %30 = OpAccessChain %15 %20 %14
+ %45 = OpCopyObject %15 %30
+ %81 = OpCopyObject %9 %27
+ %33 = OpAccessChain %15 %20 %14
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %15 %20 %14
+ %40 = OpAccessChain %15 %20 %14
+ %43 = OpAccessChain %15 %20 %14
+ %82 = OpCopyObject %9 %27
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %6 None %10
+ %11 = OpFunctionParameter %9
+ %13 = OpLabel
+ %46 = OpCopyObject %9 %11
+ %16 = OpAccessChain %15 %11 %14
+ %95 = OpCopyObject %8 %80
+ OpReturnValue %21
+ %100 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests the reference shader validity.
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // Tests |value_synonym_id| and |variable_id| are fresh ids.
+ uint32_t value_id = 21;
+ uint32_t value_synonym_id = 62;
+ uint32_t variable_id = 63;
+ uint32_t initializer_id = 23;
+ uint32_t variable_storage_class = SpvStorageClassPrivate;
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ auto transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests |value_synonym_id| and |variable_id| are non-fresh ids.
+ value_id = 80;
+ value_synonym_id = 60;
+ variable_id = 61;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // The instruction to insert before is not defined.
+ value_id = 80;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(64, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Attempting to insert the store and load instructions
+ // before an OpVariable instruction.
+ value_id = 24;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 24;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(27, SpvOpVariable, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // The block containing instruction descriptor must be reachable.
+ value_id = 80;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(100, SpvOpUnreachable, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests value instruction not available.
+ value_id = 64;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 23;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests pointer type not available.
+ value_id = 80;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassPrivate;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests not a private nor function storage class.
+ value_id = 93;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 93;
+ variable_storage_class = SpvStorageClassInput;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+#ifndef NDEBUG
+ ASSERT_DEATH(
+ transformation.IsApplicable(context.get(), transformation_context),
+ "The variable storage class must be private or function");
+#endif
+
+ // Tests value instruction not available before instruction.
+ value_id = 95;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Variable initializer is not constant.
+ value_id = 95;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 95;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Variable initializer has wrong type.
+ value_id = 95;
+ value_synonym_id = 62;
+ variable_id = 63;
+ initializer_id = 93;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationPushIdThroughVariableTest, Apply) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %92 %52 %53
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %92 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeFloat 32
+ %8 = OpTypeStruct %6 %7
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %6 %9
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 2
+ %23 = OpConstant %6 1
+ %24 = OpConstant %7 1
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Private %7
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %60 = OpConstantNull %50
+ %61 = OpUndef %51
+ %52 = OpVariable %50 Private
+ %53 = OpVariable %51 Private
+ %80 = OpConstantComposite %8 %21 %24
+ %90 = OpTypeVector %7 4
+ %91 = OpTypePointer Input %90
+ %92 = OpVariable %91 Input
+ %93 = OpConstantComposite %90 %24 %24 %24 %24
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %20 = OpVariable %9 Function
+ %27 = OpVariable %9 Function
+ OpStore %53 %21
+ %22 = OpAccessChain %15 %20 %14
+ %44 = OpCopyObject %9 %20
+ %26 = OpAccessChain %25 %20 %23
+ %29 = OpFunctionCall %6 %12 %27
+ %30 = OpAccessChain %15 %20 %14
+ %45 = OpCopyObject %15 %30
+ %81 = OpCopyObject %9 %27
+ %33 = OpAccessChain %15 %20 %14
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %15 %20 %14
+ %40 = OpAccessChain %15 %20 %14
+ %43 = OpAccessChain %15 %20 %14
+ %82 = OpCopyObject %9 %27
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %6 None %10
+ %11 = OpFunctionParameter %9
+ %13 = OpLabel
+ %46 = OpCopyObject %9 %11
+ %16 = OpAccessChain %15 %11 %14
+ %95 = OpCopyObject %8 %80
+ OpReturnValue %21
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ uint32_t value_id = 80;
+ uint32_t value_synonym_id = 100;
+ uint32_t variable_id = 101;
+ uint32_t initializer_id = 80;
+ uint32_t variable_storage_class = SpvStorageClassFunction;
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+ auto transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ value_id = 21;
+ value_synonym_id = 102;
+ variable_id = 103;
+ initializer_id = 21;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ value_id = 95;
+ value_synonym_id = 104;
+ variable_id = 105;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ value_id = 80;
+ value_synonym_id = 106;
+ variable_id = 107;
+ initializer_id = 80;
+ variable_storage_class = SpvStorageClassFunction;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ value_id = 21;
+ value_synonym_id = 108;
+ variable_id = 109;
+ initializer_id = 21;
+ variable_storage_class = SpvStorageClassPrivate;
+ instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ value_id = 23;
+ value_synonym_id = 110;
+ variable_id = 111;
+ initializer_id = 21;
+ variable_storage_class = SpvStorageClassPrivate;
+ instruction_descriptor = MakeInstructionDescriptor(27, SpvOpStore, 0);
+ transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %92 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeFloat 32
+ %8 = OpTypeStruct %6 %7
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %6 %9
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 2
+ %23 = OpConstant %6 1
+ %24 = OpConstant %7 1
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Private %7
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %60 = OpConstantNull %50
+ %61 = OpUndef %51
+ %52 = OpVariable %50 Private
+ %53 = OpVariable %51 Private
+ %80 = OpConstantComposite %8 %21 %24
+ %90 = OpTypeVector %7 4
+ %91 = OpTypePointer Input %90
+ %92 = OpVariable %91 Input
+ %93 = OpConstantComposite %90 %24 %24 %24 %24
+ %109 = OpVariable %51 Private %21
+ %111 = OpVariable %51 Private %21
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %103 = OpVariable %15 Function %21
+ %101 = OpVariable %9 Function %80
+ %20 = OpVariable %9 Function
+ %27 = OpVariable %9 Function
+ OpStore %111 %23
+ %110 = OpLoad %6 %111
+ OpStore %53 %21
+ %22 = OpAccessChain %15 %20 %14
+ %44 = OpCopyObject %9 %20
+ %26 = OpAccessChain %25 %20 %23
+ %29 = OpFunctionCall %6 %12 %27
+ %30 = OpAccessChain %15 %20 %14
+ %45 = OpCopyObject %15 %30
+ %81 = OpCopyObject %9 %27
+ %33 = OpAccessChain %15 %20 %14
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ OpStore %101 %80
+ %100 = OpLoad %8 %101
+ OpStore %103 %21
+ %102 = OpLoad %6 %103
+ %38 = OpAccessChain %15 %20 %14
+ %40 = OpAccessChain %15 %20 %14
+ %43 = OpAccessChain %15 %20 %14
+ %82 = OpCopyObject %9 %27
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %6 None %10
+ %11 = OpFunctionParameter %9
+ %13 = OpLabel
+ %107 = OpVariable %9 Function %80
+ %105 = OpVariable %9 Function %80
+ %46 = OpCopyObject %9 %11
+ %16 = OpAccessChain %15 %11 %14
+ %95 = OpCopyObject %8 %80
+ OpStore %105 %95
+ %104 = OpLoad %8 %105
+ OpStore %107 %80
+ %106 = OpLoad %8 %107
+ OpStore %109 %21
+ %108 = OpLoad %6 %109
+ OpReturnValue %21
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(80, {}),
+ MakeDataDescriptor(100, {})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(102, {})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(95, {}),
+ MakeDataDescriptor(104, {})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(80, {}),
+ MakeDataDescriptor(106, {})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(108, {})));
+}
+
+TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %92 %52 %53
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %92 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeFloat 32
+ %8 = OpTypeStruct %6 %7
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %6 %9
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 2
+ %23 = OpConstant %6 1
+ %24 = OpConstant %7 1
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Private %7
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %60 = OpConstantNull %50
+ %61 = OpUndef %51
+ %52 = OpVariable %50 Private
+ %53 = OpVariable %51 Private
+ %80 = OpConstantComposite %8 %21 %24
+ %90 = OpTypeVector %7 4
+ %91 = OpTypePointer Input %90
+ %92 = OpVariable %91 Input
+ %93 = OpConstantComposite %90 %24 %24 %24 %24
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %20 = OpVariable %9 Function
+ %27 = OpVariable %9 Function
+ %22 = OpAccessChain %15 %20 %14
+ %44 = OpCopyObject %9 %20
+ %26 = OpAccessChain %25 %20 %23
+ %29 = OpFunctionCall %6 %12 %27
+ %30 = OpAccessChain %15 %20 %14
+ %45 = OpCopyObject %15 %30
+ %81 = OpCopyObject %9 %27
+ %33 = OpAccessChain %15 %20 %14
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %15 %20 %14
+ %40 = OpAccessChain %15 %20 %14
+ %43 = OpAccessChain %15 %20 %14
+ %82 = OpCopyObject %9 %27
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %6 None %10
+ %11 = OpFunctionParameter %9
+ %13 = OpLabel
+ %46 = OpCopyObject %9 %11
+ %16 = OpAccessChain %15 %11 %14
+ %95 = OpCopyObject %8 %80
+ OpReturnValue %21
+ %100 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests the reference shader validity.
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ uint32_t value_id = 21;
+ uint32_t value_synonym_id = 62;
+ uint32_t variable_id = 63;
+ uint32_t initializer_id = 23;
+ uint32_t variable_storage_class = SpvStorageClassPrivate;
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ auto transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(62, {})));
+}
+
+TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %92 %52 %53
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %92 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeFloat 32
+ %8 = OpTypeStruct %6 %7
+ %9 = OpTypePointer Function %8
+ %10 = OpTypeFunction %6 %9
+ %14 = OpConstant %6 0
+ %15 = OpTypePointer Function %6
+ %51 = OpTypePointer Private %6
+ %21 = OpConstant %6 2
+ %23 = OpConstant %6 1
+ %24 = OpConstant %7 1
+ %25 = OpTypePointer Function %7
+ %50 = OpTypePointer Private %7
+ %34 = OpTypeBool
+ %35 = OpConstantFalse %34
+ %60 = OpConstantNull %50
+ %61 = OpUndef %51
+ %52 = OpVariable %50 Private
+ %53 = OpVariable %51 Private
+ %80 = OpConstantComposite %8 %21 %24
+ %90 = OpTypeVector %7 4
+ %91 = OpTypePointer Input %90
+ %92 = OpVariable %91 Input
+ %93 = OpConstantComposite %90 %24 %24 %24 %24
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %20 = OpVariable %9 Function
+ %27 = OpVariable %9 Function
+ %22 = OpAccessChain %15 %20 %14
+ %44 = OpCopyObject %9 %20
+ %26 = OpAccessChain %25 %20 %23
+ %29 = OpFunctionCall %6 %12 %27
+ %30 = OpAccessChain %15 %20 %14
+ %45 = OpCopyObject %15 %30
+ %81 = OpCopyObject %9 %27
+ %33 = OpAccessChain %15 %20 %14
+ OpSelectionMerge %37 None
+ OpBranchConditional %35 %36 %37
+ %36 = OpLabel
+ %38 = OpAccessChain %15 %20 %14
+ %40 = OpAccessChain %15 %20 %14
+ %43 = OpAccessChain %15 %20 %14
+ %82 = OpCopyObject %9 %27
+ OpBranch %37
+ %37 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %6 None %10
+ %11 = OpFunctionParameter %9
+ %13 = OpLabel
+ %46 = OpCopyObject %9 %11
+ %16 = OpAccessChain %15 %11 %14
+ %95 = OpCopyObject %8 %80
+ OpReturnValue %21
+ %100 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests the reference shader validity.
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ fact_manager.AddFactIdIsIrrelevant(21);
+
+ uint32_t value_id = 21;
+ uint32_t value_synonym_id = 62;
+ uint32_t variable_id = 63;
+ uint32_t initializer_id = 23;
+ uint32_t variable_storage_class = SpvStorageClassPrivate;
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(95, SpvOpReturnValue, 0);
+ auto transformation = TransformationPushIdThroughVariable(
+ value_id, value_synonym_id, variable_id, variable_storage_class,
+ initializer_id, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(62, {})));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp
new file mode 100644
index 0000000..2c309fd
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_record_synonymous_constants_test.cpp
@@ -0,0 +1,896 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+// Apply the TransformationRecordSynonymousConstants defined by the given
+// constant1_id and constant2_id and check that the fact that the two
+// constants are synonym is recorded.
+void ApplyTransformationAndCheckFactManager(
+ uint32_t constant1_id, uint32_t constant2_id, opt::IRContext* ir_context,
+ TransformationContext* transformation_context) {
+ TransformationRecordSynonymousConstants(constant1_id, constant2_id)
+ .Apply(ir_context, transformation_context);
+
+ ASSERT_TRUE(transformation_context->GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(constant1_id, {}),
+ MakeDataDescriptor(constant2_id, {})));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, IntConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %17
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %12 "c"
+ OpName %17 "color"
+ OpDecorate %8 RelaxedPrecision
+ OpDecorate %10 RelaxedPrecision
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %17 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 0
+ %19 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %18 = OpConstant %6 0
+ %11 = OpConstantNull %6
+ %13 = OpConstant %6 1
+ %20 = OpConstant %19 1
+ %21 = OpConstant %19 -1
+ %22 = OpConstant %6 1
+ %14 = OpTypeFloat 32
+ %15 = OpTypeVector %14 4
+ %16 = OpTypePointer Output %15
+ %17 = OpVariable %16 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %12 %13
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+#ifndef NDEBUG
+ // %3 is not a constant declaration
+ ASSERT_DEATH(TransformationRecordSynonymousConstants(3, 9).IsApplicable(
+ context.get(), transformation_context),
+ "The ids must refer to constants.");
+#endif
+
+#ifndef NDEBUG
+ // %3 is not a constant declaration
+ ASSERT_DEATH(TransformationRecordSynonymousConstants(9, 3).IsApplicable(
+ context.get(), transformation_context),
+ "The ids must refer to constants.");
+#endif
+
+ // The two constants must be different
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 9).IsApplicable(
+ context.get(), transformation_context));
+
+ // %9 and %13 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
+ context.get(), transformation_context));
+
+ // Swapping the ids gives the same result
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 9).IsApplicable(
+ context.get(), transformation_context));
+
+ // %11 and %13 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
+ context.get(), transformation_context));
+
+ // Swapping the ids gives the same result
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 11).IsApplicable(
+ context.get(), transformation_context));
+
+ // %20 and %21 have different values
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 21).IsApplicable(
+ context.get(), transformation_context));
+
+ // %13 and %22 are equal and thus equivalent (having the same value and type)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 22).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(13, 22, context.get(),
+ &transformation_context);
+
+ // %13 and %20 are equal even if %13 is signed and %20 is unsigned
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 20).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(13, 20, context.get(),
+ &transformation_context);
+
+ // %9 and %11 are equivalent (OpConstant with value 0 and OpConstantNull)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(9, 11, context.get(),
+ &transformation_context);
+
+ // Swapping the ids gives the same result
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 9).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(11, 9, context.get(),
+ &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, BoolConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %19
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "b"
+ OpName %19 "color"
+ OpDecorate %19 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypePointer Function %6
+ %9 = OpConstantFalse %6
+ %20 = OpConstantNull %6
+ %11 = OpConstantTrue %6
+ %21 = OpConstantFalse %6
+ %22 = OpConstantTrue %6
+ %16 = OpTypeFloat 32
+ %17 = OpTypeVector %16 4
+ %18 = OpTypePointer Output %17
+ %19 = OpVariable %18 Output
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ %10 = OpLoad %6 %8
+ %12 = OpLogicalEqual %6 %10 %11
+ OpSelectionMerge %14 None
+ OpBranchConditional %12 %13 %14
+ %13 = OpLabel
+ OpReturn
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %9 and %11 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+ context.get(), transformation_context));
+
+ // %20 and %11 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 11).IsApplicable(
+ context.get(), transformation_context));
+
+ // %9 and %21 are equivalent (both OpConstantFalse)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 21).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(9, 21, context.get(),
+ &transformation_context);
+
+ // %11 and %22 are equivalent (both OpConstantTrue)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 22).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(11, 22, context.get(),
+ &transformation_context);
+
+ // %9 and %20 are equivalent (OpConstantFalse and boolean OpConstantNull)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 20).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(9, 20, context.get(),
+ &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, FloatConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %22
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %12 "c"
+ OpName %22 "color"
+ OpDecorate %22 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstantNull %6
+ %13 = OpConstant %6 2
+ %26 = OpConstant %6 2
+ %16 = OpTypeBool
+ %20 = OpTypeVector %6 4
+ %21 = OpTypePointer Output %20
+ %22 = OpVariable %21 Output
+ %23 = OpConstantComposite %20 %9 %11 %9 %11
+ %25 = OpConstantComposite %20 %11 %9 %9 %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %12 %13
+ %14 = OpLoad %6 %8
+ %15 = OpLoad %6 %10
+ %17 = OpFOrdEqual %16 %14 %15
+ OpSelectionMerge %19 None
+ OpBranchConditional %17 %18 %24
+ %18 = OpLabel
+ OpStore %22 %23
+ OpBranch %19
+ %24 = OpLabel
+ OpStore %22 %25
+ OpBranch %19
+ %19 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %9 and %13 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
+ context.get(), transformation_context));
+
+ // %11 and %13 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
+ context.get(), transformation_context));
+
+ // %13 and %23 are not equivalent
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 23).IsApplicable(
+ context.get(), transformation_context));
+
+ // %13 and %26 are identical float constants
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 26).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(13, 26, context.get(),
+ &transformation_context);
+
+ // %9 and %11 are equivalent ()
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(9, 11, context.get(),
+ &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest,
+ VectorAndMatrixCompositeConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %24
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %12 "d"
+ OpName %16 "e"
+ OpName %24 "color"
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %24 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %28 = OpConstant %6 0
+ %30 = OpConstant %6 1
+ %10 = OpTypeInt 32 1
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 0
+ %14 = OpTypeBool
+ %15 = OpTypePointer Function %14
+ %17 = OpConstantFalse %14
+ %22 = OpTypeVector %6 4
+ %37 = OpTypeVector %6 3
+ %32 = OpTypeMatrix %22 2
+ %39 = OpTypeMatrix %22 3
+ %23 = OpTypePointer Output %22
+ %24 = OpVariable %23 Output
+ %25 = OpConstantComposite %22 %9 %9 %9 %9
+ %27 = OpConstantNull %22
+ %29 = OpConstantComposite %22 %9 %28 %28 %9
+ %31 = OpConstantComposite %22 %30 %9 %9 %9
+ %38 = OpConstantComposite %37 %9 %9 %9
+ %33 = OpConstantComposite %32 %25 %29
+ %34 = OpConstantComposite %32 %27 %25
+ %35 = OpConstantNull %32
+ %36 = OpConstantComposite %32 %31 %25
+ %40 = OpConstantComposite %39 %25 %25 %25
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %17
+ %18 = OpLoad %10 %12
+ %19 = OpIEqual %14 %18 %13
+ OpSelectionMerge %21 None
+ OpBranchConditional %19 %20 %26
+ %20 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %26 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %25 and %27 are equivalent (25 is zero-like, 27 is null)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 27).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(25, 27, context.get(),
+ &transformation_context);
+
+ // %25 and %29 are equivalent (same type and value)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 29).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(25, 29, context.get(),
+ &transformation_context);
+
+ // %27 and %29 are equivalent (27 is null, 29 is zero-like)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(27, 29).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(27, 29, context.get(),
+ &transformation_context);
+
+ // %25 and %31 are not equivalent (they have different values)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable(
+ context.get(), transformation_context));
+
+ // %27 and %31 are not equivalent (27 is null, 31 is not zero-like)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(27, 31).IsApplicable(
+ context.get(), transformation_context));
+
+ // %25 and %38 are not equivalent (they have different sizes)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 38).IsApplicable(
+ context.get(), transformation_context));
+
+ // %35 and %36 are not equivalent (35 is null, 36 has non-zero components)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+ context.get(), transformation_context));
+
+ // %33 and %36 are not equivalent (not all components are equivalent)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 36).IsApplicable(
+ context.get(), transformation_context));
+
+ // %33 and %40 are not equivalent (they have different sizes)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 40).IsApplicable(
+ context.get(), transformation_context));
+
+ // %33 and %34 are equivalent (same type, equivalent components)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 34).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(33, 34, context.get(),
+ &transformation_context);
+
+ // %33 and %35 are equivalent (33 has zero-valued components, 35 is null)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 35).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(33, 35, context.get(),
+ &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, StructCompositeConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %24
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %12 "d"
+ OpName %16 "e"
+ OpName %24 "color"
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %24 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %28 = OpConstant %6 0
+ %30 = OpConstant %6 1
+ %10 = OpTypeInt 32 1
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 0
+ %33 = OpConstantNull %10
+ %14 = OpTypeBool
+ %15 = OpTypePointer Function %14
+ %17 = OpConstantFalse %14
+ %34 = OpConstantNull %14
+ %22 = OpTypeVector %6 4
+ %32 = OpTypeStruct %22 %10 %14 %6
+ %38 = OpTypeStruct %6 %6 %6 %6
+ %23 = OpTypePointer Output %22
+ %24 = OpVariable %23 Output
+ %25 = OpConstantComposite %22 %9 %9 %9 %9
+ %27 = OpConstantNull %22
+ %29 = OpConstantComposite %22 %9 %28 %28 %9
+ %31 = OpConstantComposite %22 %30 %9 %9 %9
+ %35 = OpConstantComposite %32 %25 %13 %17 %9
+ %36 = OpConstantComposite %32 %27 %33 %34 %28
+ %37 = OpConstantComposite %32 %31 %13 %17 %9
+ %39 = OpConstantComposite %38 %9 %9 %9 %9
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %17
+ %18 = OpLoad %10 %12
+ %19 = OpIEqual %14 %18 %13
+ OpSelectionMerge %21 None
+ OpBranchConditional %19 %20 %26
+ %20 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %26 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %29 and %35 are not equivalent (they have different types)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(29, 35).IsApplicable(
+ context.get(), transformation_context));
+
+ // %35 and %37 are not equivalent (their first components are not equivalent)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 37).IsApplicable(
+ context.get(), transformation_context));
+
+ // %35 and %36 are equivalent (all their components are equivalent)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(35, 36, context.get(),
+ &transformation_context);
+
+ // %25 and %39 are not equivalent (they have different types)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 39).IsApplicable(
+ context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, ArrayCompositeConstants) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %24
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %12 "d"
+ OpName %16 "e"
+ OpName %24 "color"
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %18 RelaxedPrecision
+ OpDecorate %24 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %38 = OpConstant %6 1
+ %10 = OpTypeInt 32 1
+ %11 = OpTypePointer Function %10
+ %13 = OpConstant %10 0
+ %27 = OpConstant %10 4
+ %39 = OpConstant %10 2
+ %14 = OpTypeBool
+ %15 = OpTypePointer Function %14
+ %17 = OpConstantFalse %14
+ %22 = OpTypeVector %6 4
+ %28 = OpTypeArray %6 %27
+ %29 = OpTypeArray %28 %27
+ %40 = OpTypeArray %6 %39
+ %23 = OpTypePointer Output %22
+ %24 = OpVariable %23 Output
+ %25 = OpConstantComposite %22 %9 %9 %9 %9
+ %31 = OpConstantComposite %28 %9 %9 %9 %9
+ %41 = OpConstantComposite %40 %9 %9
+ %32 = OpConstantComposite %28 %38 %9 %9 %9
+ %33 = OpConstantNull %28
+ %34 = OpConstantComposite %29 %31 %33 %31 %33
+ %35 = OpConstantComposite %29 %33 %31 %33 %31
+ %36 = OpConstantNull %29
+ %37 = OpConstantComposite %29 %32 %33 %31 %33
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %12 = OpVariable %11 Function
+ %16 = OpVariable %15 Function
+ OpStore %8 %9
+ OpStore %12 %13
+ OpStore %16 %17
+ %18 = OpLoad %10 %12
+ %19 = OpIEqual %14 %18 %13
+ OpSelectionMerge %21 None
+ OpBranchConditional %19 %20 %26
+ %20 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %26 = OpLabel
+ OpStore %24 %25
+ OpBranch %21
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %25 and %31 are not equivalent (they have different types)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable(
+ context.get(), transformation_context));
+
+ // %25 and %41 are not equivalent (they have different sizes)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 41).IsApplicable(
+ context.get(), transformation_context));
+
+ // %31 and %32 are not equivalent (their components are not pairwise
+ // equivalent)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(31, 32).IsApplicable(
+ context.get(), transformation_context));
+
+ // %31 and %33 are equivalent (%31 has zero-valued components, 32 is null)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(31, 33).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(31, 33, context.get(),
+ &transformation_context);
+
+ // %34 and %35 are equivalent (same type, equivalent components)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(34, 35).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(34, 35, context.get(),
+ &transformation_context);
+
+ // %35 and %36 are equivalent (%36 is null, %35 has zero-valued components)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(35, 36, context.get(),
+ &transformation_context);
+
+ // %34 and %37 are not equivalent (they have non-equivalent components)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(34, 37).IsApplicable(
+ context.get(), transformation_context));
+
+ // %36 and %37 are not equivalent (36 is null, 37 does not have all-zero
+ // components)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(36, 37).IsApplicable(
+ context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, IntVectors) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main" %3
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %3 Location 0
+ %4 = OpTypeVoid
+ %5 = OpTypeFunction %4
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpTypeVector %6 4
+ %9 = OpTypeVector %7 4
+ %10 = OpTypePointer Function %8
+ %11 = OpTypePointer Function %8
+ %12 = OpConstant %6 0
+ %13 = OpConstant %7 0
+ %14 = OpConstant %6 1
+ %25 = OpConstant %7 1
+ %15 = OpConstantComposite %8 %12 %12 %12 %12
+ %16 = OpConstantComposite %9 %13 %13 %13 %13
+ %17 = OpConstantComposite %8 %14 %12 %12 %14
+ %18 = OpConstantComposite %9 %25 %13 %13 %25
+ %19 = OpConstantNull %8
+ %20 = OpConstantNull %9
+ %21 = OpTypeFloat 32
+ %22 = OpTypeVector %21 4
+ %23 = OpTypePointer Output %22
+ %3 = OpVariable %23 Output
+ %2 = OpFunction %4 None %5
+ %24 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // %15 and %17 are not equivalent (having non-equivalent components)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(15, 17).IsApplicable(
+ context.get(), transformation_context));
+
+ // %17 and %19 are not equivalent (%19 is null, %17 is non-zero)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 19).IsApplicable(
+ context.get(), transformation_context));
+
+ // %17 and %20 are not equivalent (%19 is null, %20 is non-zero)
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 20).IsApplicable(
+ context.get(), transformation_context));
+
+ // %15 and %16 are equivalent (having pairwise equivalent components)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 16).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(15, 16, context.get(),
+ &transformation_context);
+
+ // %17 and %18 are equivalent (having pairwise equivalent components)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(17, 18).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(17, 18, context.get(),
+ &transformation_context);
+
+ // %19 and %20 are equivalent (both null vectors with compatible types)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(19, 20).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(19, 20, context.get(),
+ &transformation_context);
+
+ // %15 and %19 are equivalent (they have compatible types, %15 is zero-like
+ // and %19 is null)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 19).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(15, 19, context.get(),
+ &transformation_context);
+
+ // %15 and %20 are equivalent (they have compatible types, %15 is zero-like
+ // and %20 is null)
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 20).IsApplicable(
+ context.get(), transformation_context));
+
+ ApplyTransformationAndCheckFactManager(15, 20, context.get(),
+ &transformation_context);
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, FirstIrrelevantConstant) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpConstant %6 23
+ %8 = OpConstant %6 23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+ context.get(), transformation_context));
+
+ fact_manager.AddFactIdIsIrrelevant(7);
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+ context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, SecondIrrelevantConstant) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpConstant %6 23
+ %8 = OpConstant %6 23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+ context.get(), transformation_context));
+
+ fact_manager.AddFactIdIsIrrelevant(8);
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+ context.get(), transformation_context));
+}
+
+TEST(TransformationRecordSynonymousConstantsTest, InvalidIds) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpConstant %6 23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable(
+ context.get(), transformation_context));
+
+ ASSERT_FALSE(TransformationRecordSynonymousConstants(8, 7).IsApplicable(
+ context.get(), transformation_context));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
index b320308..22815e6 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
@@ -616,41 +616,41 @@
// Hand-written SPIR-V to check applicability of the transformation on an
// OpPhi argument.
- std::string shader = R"(
+ std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %4 "main"
- OpExecutionMode %4 OriginUpperLeft
- OpSource ESSL 310
- OpName %4 "main"
+ OpEntryPoint Vertex %10 "main"
+
+; Types
%2 = OpTypeVoid
%3 = OpTypeFunction %2
- %6 = OpTypeBool
- %7 = OpTypePointer Function %6
- %9 = OpConstantTrue %6
- %16 = OpConstantFalse %6
- %10 = OpTypeInt 32 1
- %11 = OpTypePointer Function %10
- %13 = OpConstant %10 0
- %15 = OpConstant %10 1
- %4 = OpFunction %2 None %3
- %5 = OpLabel
- OpSelectionMerge %20 None
- OpBranchConditional %9 %21 %22
- %21 = OpLabel
- OpBranch %20
- %22 = OpLabel
- OpBranch %20
- %20 = OpLabel
- %23 = OpPhi %6 %9 %21 %16 %22
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeBool
+
+; Constants
+ %6 = OpConstant %4 0
+ %7 = OpConstant %4 1
+ %8 = OpConstantTrue %5
+ %9 = OpConstantFalse %5
+
+; main function
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ OpSelectionMerge %13 None
+ OpBranchConditional %8 %12 %13
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %14 = OpPhi %5 %8 %11 %9 %12
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
@@ -658,11 +658,48 @@
TransformationContext transformation_context(&fact_manager,
validator_options);
- auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
- MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
- 15, SpvOpSLessThan, 100);
+ auto instruction_descriptor = MakeInstructionDescriptor(14, SpvOpPhi, 0);
+ auto id_use_descriptor = MakeIdUseDescriptor(8, instruction_descriptor, 0);
+ auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
+ id_use_descriptor, 6, 7, SpvOpULessThan, 15);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
- ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context));
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %10 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeInt 32 0
+ %5 = OpTypeBool
+
+; Constants
+ %6 = OpConstant %4 0
+ %7 = OpConstant %4 1
+ %8 = OpConstantTrue %5
+ %9 = OpConstantFalse %5
+
+; main function
+ %10 = OpFunction %2 None %3
+ %11 = OpLabel
+ %15 = OpULessThan %5 %6 %7
+ OpSelectionMerge %13 None
+ OpBranchConditional %8 %12 %13
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %14 = OpPhi %5 %15 %11 %9 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
new file mode 100644
index 0000000..2bbe605
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp
@@ -0,0 +1,151 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceCopyMemoryWithLoadStoreTest, BasicScenarios) {
+ // This is a simple transformation and this test handles the main cases.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ OpCopyMemory %8 %10
+ OpCopyMemory %16 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto instruction_descriptor_invalid_1 =
+ MakeInstructionDescriptor(5, SpvOpStore, 0);
+ auto instruction_descriptor_valid_1 =
+ MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+ auto instruction_descriptor_valid_2 =
+ MakeInstructionDescriptor(5, SpvOpCopyMemory, 0);
+
+ // Invalid: |source_id| is not a fresh id.
+ auto transformation_invalid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+ 15, instruction_descriptor_valid_1);
+ ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: |instruction_descriptor_invalid| refers to an instruction OpStore.
+ auto transformation_invalid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+ 20, instruction_descriptor_invalid_1);
+ ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
+ transformation_context));
+
+ auto transformation_valid_1 = TransformationReplaceCopyMemoryWithLoadStore(
+ 20, instruction_descriptor_valid_1);
+ ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_valid_2 = TransformationReplaceCopyMemoryWithLoadStore(
+ 21, instruction_descriptor_valid_2);
+ ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ %20 = OpLoad %6 %10
+ OpStore %8 %20
+ %21 = OpLoad %12 %14
+ OpStore %16 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
new file mode 100644
index 0000000..d1d38a9
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp
@@ -0,0 +1,197 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceCopyObjectWithStoreLoad, BasicScenarios) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %23
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ OpName %18 "e"
+ OpName %23 "f"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %22 = OpTypePointer Private %12
+ %23 = OpVariable %22 Private
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ %18 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ %19 = OpLoad %6 %8
+ %20 = OpLoad %6 %10
+ %21 = OpIAdd %6 %19 %20
+ OpStore %18 %21
+ %24 = OpLoad %12 %14
+ %25 = OpLoad %12 %16
+ %26 = OpFMul %12 %24 %25
+ OpStore %23 %26
+ %27 = OpCopyObject %6 %21
+ %28 = OpCopyObject %12 %26
+ %40 = OpCopyObject %13 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // Invalid: fresh_variable_id=10 is not fresh.
+ auto transformation_invalid_1 = TransformationReplaceCopyObjectWithStoreLoad(
+ 27, 10, SpvStorageClassFunction, 9);
+ ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: copy_object_result_id=26 is not a CopyObject instruction.
+ auto transformation_invalid_2 = TransformationReplaceCopyObjectWithStoreLoad(
+ 26, 30, SpvStorageClassFunction, 9);
+ ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: copy_object_result_id=40 is of type pointer.
+ auto transformation_invalid_3 = TransformationReplaceCopyObjectWithStoreLoad(
+ 40, 30, SpvStorageClassFunction, 9);
+ ASSERT_FALSE(transformation_invalid_3.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: Pointer type instruction in this storage class pointing to the
+ // value type is not defined.
+ auto transformation_invalid_4 = TransformationReplaceCopyObjectWithStoreLoad(
+ 40, 30, SpvStorageClassPrivate, 9);
+ ASSERT_FALSE(transformation_invalid_4.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: initializer_id=15 is invalid.
+ auto transformation_invalid_5 = TransformationReplaceCopyObjectWithStoreLoad(
+ 27, 30, SpvStorageClassPrivate, 15);
+ ASSERT_FALSE(transformation_invalid_5.IsApplicable(context.get(),
+ transformation_context));
+
+ // Invalid: SpvStorageClassUniform is not applicable to the transformation.
+ auto transformation_invalid_6 = TransformationReplaceCopyObjectWithStoreLoad(
+ 27, 30, SpvStorageClassUniform, 9);
+ ASSERT_FALSE(transformation_invalid_6.IsApplicable(context.get(),
+ transformation_context));
+
+ auto transformation_valid_1 = TransformationReplaceCopyObjectWithStoreLoad(
+ 27, 30, SpvStorageClassFunction, 9);
+ ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_valid_2 = TransformationReplaceCopyObjectWithStoreLoad(
+ 28, 32, SpvStorageClassPrivate, 15);
+ ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(),
+ transformation_context));
+ transformation_valid_2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %23 %32
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %14 "c"
+ OpName %16 "d"
+ OpName %18 "e"
+ OpName %23 "f"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %12 = OpTypeFloat 32
+ %13 = OpTypePointer Function %12
+ %15 = OpConstant %12 2
+ %17 = OpConstant %12 3
+ %22 = OpTypePointer Private %12
+ %23 = OpVariable %22 Private
+ %32 = OpVariable %22 Private %15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %30 = OpVariable %7 Function %9
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %14 = OpVariable %13 Function
+ %16 = OpVariable %13 Function
+ %18 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %14 %15
+ OpStore %16 %17
+ %19 = OpLoad %6 %8
+ %20 = OpLoad %6 %10
+ %21 = OpIAdd %6 %19 %20
+ OpStore %18 %21
+ %24 = OpLoad %12 %14
+ %25 = OpLoad %12 %16
+ %26 = OpFMul %12 %24 %25
+ OpStore %23 %26
+ OpStore %30 %21
+ %27 = OpLoad %6 %30
+ OpStore %32 %26
+ %28 = OpLoad %12 %32
+ %40 = OpCopyObject %13 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index 37e9510..e4a3f00 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -1450,6 +1450,271 @@
.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerConstants) {
+ // This checks that replacing an integer constant with an equivalent one with
+ // different signedness is allowed only when valid.
+ const std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %2 "main"
+ OpName %3 "a"
+ OpDecorate %3 RelaxedPrecision
+ %4 = OpTypeVoid
+ %5 = OpTypeBool
+ %6 = OpConstantTrue %5
+ %7 = OpTypeFunction %4
+ %8 = OpTypeInt 32 1
+ %9 = OpTypePointer Function %8
+ %10 = OpConstant %8 1
+ %11 = OpTypeInt 32 0
+ %12 = OpTypePointer Function %11
+ %13 = OpConstant %11 1
+ %2 = OpFunction %4 None %7
+ %14 = OpLabel
+ %3 = OpVariable %9 Function
+ %15 = OpSNegate %8 %10
+ %16 = OpIAdd %8 %10 %10
+ %17 = OpSDiv %8 %10 %10
+ %18 = OpUDiv %11 %13 %13
+ %19 = OpBitwiseAnd %8 %10 %10
+ %20 = OpSelect %8 %6 %10 %17
+ %21 = OpIEqual %5 %10 %10
+ OpStore %3 %10
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Add synonym fact relating %10 and %13 (equivalent integer constant with
+ // different signedness).
+ transformation_context.GetFactManager()->AddFact(MakeSynonymFact(10, 13),
+ context.get());
+
+ // Legal because OpSNegate always considers the integer as signed
+ auto replacement1 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(10, MakeInstructionDescriptor(15, SpvOpSNegate, 0),
+ 0),
+ 13);
+ ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+ replacement1.Apply(context.get(), &transformation_context);
+
+ // Legal because OpIAdd does not care about the signedness of the operands
+ auto replacement2 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 0),
+ 13);
+ ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+ replacement2.Apply(context.get(), &transformation_context);
+
+ // Legal because OpSDiv does not care about the signedness of the operands
+ auto replacement3 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, SpvOpSDiv, 0), 0),
+ 13);
+ ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context));
+ replacement3.Apply(context.get(), &transformation_context);
+
+ // Not legal because OpUDiv requires unsigned integers
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(
+ 13, MakeInstructionDescriptor(18, SpvOpUDiv, 0), 0),
+ 10)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Legal because OpSDiv does not care about the signedness of the operands
+ auto replacement4 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(10, MakeInstructionDescriptor(19, SpvOpBitwiseAnd, 0),
+ 0),
+ 13);
+ ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context));
+ replacement4.Apply(context.get(), &transformation_context);
+
+ // Not legal because OpSelect requires both operands to have the same type as
+ // the result type
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(
+ 10, MakeInstructionDescriptor(20, SpvOpUDiv, 0), 1),
+ 13)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Not legal because OpStore requires the object to match the type pointed
+ // to by the pointer.
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(
+ 10, MakeInstructionDescriptor(21, SpvOpStore, 0), 1),
+ 13)
+ .IsApplicable(context.get(), transformation_context));
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ const std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %2 "main"
+ OpName %3 "a"
+ OpDecorate %3 RelaxedPrecision
+ %4 = OpTypeVoid
+ %5 = OpTypeBool
+ %6 = OpConstantTrue %5
+ %7 = OpTypeFunction %4
+ %8 = OpTypeInt 32 1
+ %9 = OpTypePointer Function %8
+ %10 = OpConstant %8 1
+ %11 = OpTypeInt 32 0
+ %12 = OpTypePointer Function %11
+ %13 = OpConstant %11 1
+ %2 = OpFunction %4 None %7
+ %14 = OpLabel
+ %3 = OpVariable %9 Function
+ %15 = OpSNegate %8 %13
+ %16 = OpIAdd %8 %13 %10
+ %17 = OpSDiv %8 %13 %10
+ %18 = OpUDiv %11 %13 %13
+ %19 = OpBitwiseAnd %8 %13 %10
+ %20 = OpSelect %8 %6 %10 %17
+ %21 = OpIEqual %5 %10 %10
+ OpStore %3 %10
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerVectorConstants) {
+ // This checks that replacing an integer constant with an equivalent one with
+ // different signedness is allowed only when valid.
+ const std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %2 "main"
+ OpName %3 "a"
+ OpDecorate %3 RelaxedPrecision
+ OpDecorate %4 RelaxedPrecision
+ %5 = OpTypeVoid
+ %6 = OpTypeFunction %5
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeInt 32 0
+ %9 = OpTypeVector %7 4
+ %10 = OpTypeVector %8 4
+ %11 = OpTypePointer Function %9
+ %12 = OpConstant %7 1
+ %13 = OpConstant %8 1
+ %14 = OpConstantComposite %9 %12 %12 %12 %12
+ %15 = OpConstantComposite %10 %13 %13 %13 %13
+ %16 = OpTypePointer Function %7
+ %2 = OpFunction %5 None %6
+ %17 = OpLabel
+ %3 = OpVariable %11 Function
+ %18 = OpIAdd %9 %14 %14
+ OpStore %3 %14
+ %19 = OpAccessChain %16 %3 %13
+ %4 = OpLoad %7 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Add synonym fact relating %10 and %13 (equivalent integer vectors with
+ // different signedness).
+ transformation_context.GetFactManager()->AddFact(MakeSynonymFact(14, 15),
+ context.get());
+
+ // Legal because OpIAdd does not consider the signedness of the operands
+ auto replacement1 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, SpvOpIAdd, 0), 0),
+ 15);
+ ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context));
+ replacement1.Apply(context.get(), &transformation_context);
+
+ // Not legal because OpStore requires the object to match the type pointed
+ // to by the pointer.
+ ASSERT_FALSE(TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(
+ 14, MakeInstructionDescriptor(18, SpvOpStore, 0), 1),
+ 15)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Add synonym fact relating %12 and %13 (equivalent integer constants with
+ // different signedness).
+ transformation_context.GetFactManager()->AddFact(MakeSynonymFact(12, 13),
+ context.get());
+
+ // Legal because the indices of OpAccessChain are always treated as signed
+ auto replacement2 = TransformationReplaceIdWithSynonym(
+ MakeIdUseDescriptor(
+ 13, MakeInstructionDescriptor(19, SpvOpAccessChain, 0), 1),
+ 12);
+ ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context));
+ replacement2.Apply(context.get(), &transformation_context);
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ const std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %2 "main"
+ OpName %3 "a"
+ OpDecorate %3 RelaxedPrecision
+ OpDecorate %4 RelaxedPrecision
+ %5 = OpTypeVoid
+ %6 = OpTypeFunction %5
+ %7 = OpTypeInt 32 1
+ %8 = OpTypeInt 32 0
+ %9 = OpTypeVector %7 4
+ %10 = OpTypeVector %8 4
+ %11 = OpTypePointer Function %9
+ %12 = OpConstant %7 1
+ %13 = OpConstant %8 1
+ %14 = OpConstantComposite %9 %12 %12 %12 %12
+ %15 = OpConstantComposite %10 %13 %13 %13 %13
+ %16 = OpTypePointer Function %7
+ %2 = OpFunction %5 None %6
+ %17 = OpLabel
+ %3 = OpVariable %11 Function
+ %18 = OpIAdd %9 %15 %14
+ OpStore %3 %14
+ %19 = OpAccessChain %16 %3 %12
+ %4 = OpLoad %7 %19
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
new file mode 100644
index 0000000..0b04e96
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp
@@ -0,0 +1,1876 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest, IsApplicable) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %22 "main"
+ OpExecutionMode %22 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %22 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpConstant %4 1
+ %9 = OpConstant %4 2
+ %10 = OpConstant %4 3
+ %11 = OpConstant %4 4
+ %12 = OpConstant %4 5
+ %13 = OpConstant %4 6
+ %14 = OpConstant %4 7
+ %15 = OpConstant %4 8
+ %16 = OpConstantComposite %5 %8 %9
+ %17 = OpConstantComposite %5 %10 %11
+ %18 = OpConstantComposite %6 %8 %9 %10
+ %19 = OpConstantComposite %6 %11 %12 %13
+ %20 = OpConstantComposite %7 %8 %9 %10 %11
+ %21 = OpConstantComposite %7 %12 %13 %14 %15
+ %22 = OpFunction %2 None %3
+ %23 = OpLabel
+ %24 = OpDot %4 %16 %17
+ %25 = OpDot %4 %18 %19
+ %26 = OpDot %4 %20 %21
+ %27 = OpVectorTimesScalar %5 %16 %8
+ %28 = OpVectorTimesScalar %6 %18 %9
+ %29 = OpVectorTimesScalar %7 %20 %10
+ %30 = OpCopyObject %4 %24
+ %31 = OpFAdd %4 %8 %9
+ %32 = OpFMul %4 %10 %11
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests linear algebra instructions.
+ auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 37, 38}, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(27, SpvOpVectorTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36}, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non-linear algebra instructions.
+ instruction_descriptor = MakeInstructionDescriptor(30, SpvOpCopyObject, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 37, 38}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor = MakeInstructionDescriptor(31, SpvOpFAdd, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 37}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor = MakeInstructionDescriptor(32, SpvOpFMul, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests number of fresh ids is different than necessary.
+ instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(28, SpvOpVectorTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 37, 38, 39}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non-fresh ids.
+ instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 5, 36, 37, 8, 39, 40, 1, 42, 3, 44, 45, 46},
+ instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(29, SpvOpVectorTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 7, 38, 9, 40}, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+ ReplaceOpVectorTimesScalar) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %15 "main"
+ OpExecutionMode %15 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %15 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpConstant %4 1
+ %9 = OpConstant %4 2
+ %10 = OpConstant %4 3
+ %11 = OpConstant %4 4
+ %12 = OpConstantComposite %5 %8 %9
+ %13 = OpConstantComposite %6 %8 %9 %10
+ %14 = OpConstantComposite %7 %8 %9 %10 %11
+ %15 = OpFunction %2 None %3
+ %16 = OpLabel
+ %17 = OpVectorTimesScalar %5 %12 %8
+ %18 = OpVectorTimesScalar %6 %13 %9
+ %19 = OpVectorTimesScalar %7 %14 %10
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(17, SpvOpVectorTimesScalar, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {20, 21, 22, 23}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(18, SpvOpVectorTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {24, 25, 26, 27, 28, 29}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(19, SpvOpVectorTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {30, 31, 32, 33, 34, 35, 36, 37}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %15 "main"
+ OpExecutionMode %15 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %15 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpConstant %4 1
+ %9 = OpConstant %4 2
+ %10 = OpConstant %4 3
+ %11 = OpConstant %4 4
+ %12 = OpConstantComposite %5 %8 %9
+ %13 = OpConstantComposite %6 %8 %9 %10
+ %14 = OpConstantComposite %7 %8 %9 %10 %11
+ %15 = OpFunction %2 None %3
+ %16 = OpLabel
+ %20 = OpCompositeExtract %4 %12 0
+ %21 = OpFMul %4 %20 %8
+ %22 = OpCompositeExtract %4 %12 1
+ %23 = OpFMul %4 %22 %8
+ %17 = OpCompositeConstruct %5 %21 %23
+ %24 = OpCompositeExtract %4 %13 0
+ %25 = OpFMul %4 %24 %9
+ %26 = OpCompositeExtract %4 %13 1
+ %27 = OpFMul %4 %26 %9
+ %28 = OpCompositeExtract %4 %13 2
+ %29 = OpFMul %4 %28 %9
+ %18 = OpCompositeConstruct %6 %25 %27 %29
+ %30 = OpCompositeExtract %4 %14 0
+ %31 = OpFMul %4 %30 %10
+ %32 = OpCompositeExtract %4 %14 1
+ %33 = OpFMul %4 %32 %10
+ %34 = OpCompositeExtract %4 %14 2
+ %35 = OpFMul %4 %34 %10
+ %36 = OpCompositeExtract %4 %14 3
+ %37 = OpFMul %4 %36 %10
+ %19 = OpCompositeConstruct %7 %31 %33 %35 %37
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+ ReplaceOpMatrixTimesScalar) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2-row matrices by scalar
+ %56 = OpMatrixTimesScalar %8 %45 %17
+ %57 = OpMatrixTimesScalar %9 %46 %18
+ %58 = OpMatrixTimesScalar %10 %47 %19
+
+; Multiplying 3-row matrices by scalar
+ %59 = OpMatrixTimesScalar %11 %48 %21
+ %60 = OpMatrixTimesScalar %12 %49 %22
+ %61 = OpMatrixTimesScalar %13 %50 %23
+
+; Multiplying 4-row matrices by scalar
+ %62 = OpMatrixTimesScalar %14 %51 %24
+ %63 = OpMatrixTimesScalar %15 %52 %25
+ %64 = OpMatrixTimesScalar %16 %53 %26
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(56, SpvOpMatrixTimesScalar, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(57, SpvOpMatrixTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(58, SpvOpMatrixTimesScalar, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2x2 matrix by scalar
+ %65 = OpCompositeExtract %5 %45 0
+ %66 = OpCompositeExtract %4 %65 0
+ %67 = OpFMul %4 %66 %17
+ %68 = OpCompositeExtract %4 %65 1
+ %69 = OpFMul %4 %68 %17
+ %70 = OpCompositeConstruct %5 %67 %69
+ %71 = OpCompositeExtract %5 %45 1
+ %72 = OpCompositeExtract %4 %71 0
+ %73 = OpFMul %4 %72 %17
+ %74 = OpCompositeExtract %4 %71 1
+ %75 = OpFMul %4 %74 %17
+ %76 = OpCompositeConstruct %5 %73 %75
+ %56 = OpCompositeConstruct %8 %70 %76
+
+; Multiplying 2x3 matrix by scalar
+ %77 = OpCompositeExtract %5 %46 0
+ %78 = OpCompositeExtract %4 %77 0
+ %79 = OpFMul %4 %78 %18
+ %80 = OpCompositeExtract %4 %77 1
+ %81 = OpFMul %4 %80 %18
+ %82 = OpCompositeConstruct %5 %79 %81
+ %83 = OpCompositeExtract %5 %46 1
+ %84 = OpCompositeExtract %4 %83 0
+ %85 = OpFMul %4 %84 %18
+ %86 = OpCompositeExtract %4 %83 1
+ %87 = OpFMul %4 %86 %18
+ %88 = OpCompositeConstruct %5 %85 %87
+ %89 = OpCompositeExtract %5 %46 2
+ %90 = OpCompositeExtract %4 %89 0
+ %91 = OpFMul %4 %90 %18
+ %92 = OpCompositeExtract %4 %89 1
+ %93 = OpFMul %4 %92 %18
+ %94 = OpCompositeConstruct %5 %91 %93
+ %57 = OpCompositeConstruct %9 %82 %88 %94
+
+; Multiplying 2x4 matrix by scalar
+ %95 = OpCompositeExtract %5 %47 0
+ %96 = OpCompositeExtract %4 %95 0
+ %97 = OpFMul %4 %96 %19
+ %98 = OpCompositeExtract %4 %95 1
+ %99 = OpFMul %4 %98 %19
+ %100 = OpCompositeConstruct %5 %97 %99
+ %101 = OpCompositeExtract %5 %47 1
+ %102 = OpCompositeExtract %4 %101 0
+ %103 = OpFMul %4 %102 %19
+ %104 = OpCompositeExtract %4 %101 1
+ %105 = OpFMul %4 %104 %19
+ %106 = OpCompositeConstruct %5 %103 %105
+ %107 = OpCompositeExtract %5 %47 2
+ %108 = OpCompositeExtract %4 %107 0
+ %109 = OpFMul %4 %108 %19
+ %110 = OpCompositeExtract %4 %107 1
+ %111 = OpFMul %4 %110 %19
+ %112 = OpCompositeConstruct %5 %109 %111
+ %113 = OpCompositeExtract %5 %47 3
+ %114 = OpCompositeExtract %4 %113 0
+ %115 = OpFMul %4 %114 %19
+ %116 = OpCompositeExtract %4 %113 1
+ %117 = OpFMul %4 %116 %19
+ %118 = OpCompositeConstruct %5 %115 %117
+ %58 = OpCompositeConstruct %10 %100 %106 %112 %118
+
+; Multiplying 3-row matrices by scalar
+ %59 = OpMatrixTimesScalar %11 %48 %21
+ %60 = OpMatrixTimesScalar %12 %49 %22
+ %61 = OpMatrixTimesScalar %13 %50 %23
+
+; Multiplying 4-row matrices by scalar
+ %62 = OpMatrixTimesScalar %14 %51 %24
+ %63 = OpMatrixTimesScalar %15 %52 %25
+ %64 = OpMatrixTimesScalar %16 %53 %26
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+ ReplaceOpVectorTimesMatrix) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2-dimensional vector by 2x2 matrix
+ %56 = OpVectorTimesMatrix %5 %33 %45
+
+; Multiplying 2-dimensional vector by 2x3 matrix
+ %57 = OpVectorTimesMatrix %6 %34 %46
+
+; Multiplying 2-dimensional vector by 2x4 matrix
+ %58 = OpVectorTimesMatrix %7 %35 %47
+
+; Multiplying 3-dimensional vector by 3x2 matrix
+ %59 = OpVectorTimesMatrix %5 %37 %48
+
+; Multiplying 3-dimensional vector by 3x3 matrix
+ %60 = OpVectorTimesMatrix %6 %38 %49
+
+; Multiplying 3-dimensional vector by 3x4 matrix
+ %61 = OpVectorTimesMatrix %7 %39 %50
+
+; Multiplying 4-dimensional vector by 4x2 matrix
+ %62 = OpVectorTimesMatrix %5 %41 %51
+
+; Multiplying 4-dimensional vector by 4x3 matrix
+ %63 = OpVectorTimesMatrix %6 %42 %52
+
+; Multiplying 4-dimensional vector by 4x4 matrix
+ %64 = OpVectorTimesMatrix %7 %43 %53
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(56, SpvOpVectorTimesMatrix, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(57, SpvOpVectorTimesMatrix, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(58, SpvOpVectorTimesMatrix, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(59, SpvOpVectorTimesMatrix, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2-dimensional vector by 2x2 matrix
+ %65 = OpCompositeExtract %4 %33 0
+ %66 = OpCompositeExtract %4 %33 1
+ %67 = OpCompositeExtract %5 %45 0
+ %68 = OpCompositeExtract %4 %67 0
+ %69 = OpFMul %4 %65 %68
+ %70 = OpCompositeExtract %4 %67 1
+ %71 = OpFMul %4 %66 %70
+ %72 = OpFAdd %4 %69 %71
+ %73 = OpCompositeExtract %5 %45 1
+ %74 = OpCompositeExtract %4 %73 0
+ %75 = OpFMul %4 %65 %74
+ %76 = OpCompositeExtract %4 %73 1
+ %77 = OpFMul %4 %66 %76
+ %78 = OpFAdd %4 %75 %77
+ %56 = OpCompositeConstruct %5 %72 %78
+
+; Multiplying 2-dimensional vector by 2x3 matrix
+ %79 = OpCompositeExtract %4 %34 0
+ %80 = OpCompositeExtract %4 %34 1
+ %81 = OpCompositeExtract %5 %46 0
+ %82 = OpCompositeExtract %4 %81 0
+ %83 = OpFMul %4 %79 %82
+ %84 = OpCompositeExtract %4 %81 1
+ %85 = OpFMul %4 %80 %84
+ %86 = OpFAdd %4 %83 %85
+ %87 = OpCompositeExtract %5 %46 1
+ %88 = OpCompositeExtract %4 %87 0
+ %89 = OpFMul %4 %79 %88
+ %90 = OpCompositeExtract %4 %87 1
+ %91 = OpFMul %4 %80 %90
+ %92 = OpFAdd %4 %89 %91
+ %93 = OpCompositeExtract %5 %46 2
+ %94 = OpCompositeExtract %4 %93 0
+ %95 = OpFMul %4 %79 %94
+ %96 = OpCompositeExtract %4 %93 1
+ %97 = OpFMul %4 %80 %96
+ %98 = OpFAdd %4 %95 %97
+ %57 = OpCompositeConstruct %6 %86 %92 %98
+
+; Multiplying 2-dimensional vector by 2x4 matrix
+ %99 = OpCompositeExtract %4 %35 0
+ %100 = OpCompositeExtract %4 %35 1
+ %101 = OpCompositeExtract %5 %47 0
+ %102 = OpCompositeExtract %4 %101 0
+ %103 = OpFMul %4 %99 %102
+ %104 = OpCompositeExtract %4 %101 1
+ %105 = OpFMul %4 %100 %104
+ %106 = OpFAdd %4 %103 %105
+ %107 = OpCompositeExtract %5 %47 1
+ %108 = OpCompositeExtract %4 %107 0
+ %109 = OpFMul %4 %99 %108
+ %110 = OpCompositeExtract %4 %107 1
+ %111 = OpFMul %4 %100 %110
+ %112 = OpFAdd %4 %109 %111
+ %113 = OpCompositeExtract %5 %47 2
+ %114 = OpCompositeExtract %4 %113 0
+ %115 = OpFMul %4 %99 %114
+ %116 = OpCompositeExtract %4 %113 1
+ %117 = OpFMul %4 %100 %116
+ %118 = OpFAdd %4 %115 %117
+ %119 = OpCompositeExtract %5 %47 3
+ %120 = OpCompositeExtract %4 %119 0
+ %121 = OpFMul %4 %99 %120
+ %122 = OpCompositeExtract %4 %119 1
+ %123 = OpFMul %4 %100 %122
+ %124 = OpFAdd %4 %121 %123
+ %58 = OpCompositeConstruct %7 %106 %112 %118 %124
+
+; Multiplying 3-dimensional vector by 3x2 matrix
+ %125 = OpCompositeExtract %4 %37 0
+ %126 = OpCompositeExtract %4 %37 1
+ %127 = OpCompositeExtract %4 %37 2
+ %128 = OpCompositeExtract %6 %48 0
+ %129 = OpCompositeExtract %4 %128 0
+ %130 = OpFMul %4 %125 %129
+ %131 = OpCompositeExtract %4 %128 1
+ %132 = OpFMul %4 %126 %131
+ %133 = OpCompositeExtract %4 %128 2
+ %134 = OpFMul %4 %127 %133
+ %135 = OpFAdd %4 %130 %132
+ %136 = OpFAdd %4 %134 %135
+ %137 = OpCompositeExtract %6 %48 1
+ %138 = OpCompositeExtract %4 %137 0
+ %139 = OpFMul %4 %125 %138
+ %140 = OpCompositeExtract %4 %137 1
+ %141 = OpFMul %4 %126 %140
+ %142 = OpCompositeExtract %4 %137 2
+ %143 = OpFMul %4 %127 %142
+ %144 = OpFAdd %4 %139 %141
+ %145 = OpFAdd %4 %143 %144
+ %59 = OpCompositeConstruct %5 %136 %145
+
+; Multiplying 3-dimensional vector by 3x3 matrix
+ %60 = OpVectorTimesMatrix %6 %38 %49
+
+; Multiplying 3-dimensional vector by 3x4 matrix
+ %61 = OpVectorTimesMatrix %7 %39 %50
+
+; Multiplying 4-dimensional vector by 4x2 matrix
+ %62 = OpVectorTimesMatrix %5 %41 %51
+
+; Multiplying 4-dimensional vector by 4x3 matrix
+ %63 = OpVectorTimesMatrix %6 %42 %52
+
+; Multiplying 4-dimensional vector by 4x4 matrix
+ %64 = OpVectorTimesMatrix %7 %43 %53
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+ ReplaceOpMatrixTimesVector) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2-dimensional vector
+ %56 = OpMatrixTimesVector %5 %45 %33
+
+; Multiplying 3x2 matrix by 2-dimensional vector
+ %57 = OpMatrixTimesVector %6 %48 %34
+
+; Multiplying 4x2 matrix by 2-dimensional vector
+ %58 = OpMatrixTimesVector %7 %51 %35
+
+; Multiplying 2x3 matrix by 3-dimensional vector
+ %59 = OpMatrixTimesVector %5 %46 %37
+
+; Multiplying 3x3 matrix by 3-dimensional vector
+ %60 = OpMatrixTimesVector %6 %49 %38
+
+; Multiplying 4x3 matrix by 3-dimensional vector
+ %61 = OpMatrixTimesVector %7 %52 %39
+
+; Multiplying 2x4 matrix by 4-dimensional vector
+ %62 = OpMatrixTimesVector %5 %47 %41
+
+; Multiplying 3x4 matrix by 4-dimensional vector
+ %63 = OpMatrixTimesVector %6 %50 %42
+
+; Multiplying 4x4 matrix by 4-dimensional vector
+ %64 = OpMatrixTimesVector %7 %53 %43
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(56, SpvOpMatrixTimesVector, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(57, SpvOpMatrixTimesVector, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(58, SpvOpMatrixTimesVector, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(59, SpvOpMatrixTimesVector, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2-dimensional vector
+ %65 = OpCompositeExtract %5 %45 0
+ %66 = OpCompositeExtract %5 %45 1
+ %67 = OpCompositeExtract %4 %33 0
+ %68 = OpCompositeExtract %4 %33 1
+ %69 = OpCompositeExtract %4 %65 0
+ %70 = OpFMul %4 %69 %67
+ %71 = OpCompositeExtract %4 %66 0
+ %72 = OpFMul %4 %71 %68
+ %73 = OpFAdd %4 %70 %72
+ %74 = OpCompositeExtract %4 %65 1
+ %75 = OpFMul %4 %74 %67
+ %76 = OpCompositeExtract %4 %66 1
+ %77 = OpFMul %4 %76 %68
+ %78 = OpFAdd %4 %75 %77
+ %56 = OpCompositeConstruct %5 %73 %78
+
+; Multiplying 3x2 matrix by 2-dimensional vector
+ %79 = OpCompositeExtract %6 %48 0
+ %80 = OpCompositeExtract %6 %48 1
+ %81 = OpCompositeExtract %4 %34 0
+ %82 = OpCompositeExtract %4 %34 1
+ %83 = OpCompositeExtract %4 %79 0
+ %84 = OpFMul %4 %83 %81
+ %85 = OpCompositeExtract %4 %80 0
+ %86 = OpFMul %4 %85 %82
+ %87 = OpFAdd %4 %84 %86
+ %88 = OpCompositeExtract %4 %79 1
+ %89 = OpFMul %4 %88 %81
+ %90 = OpCompositeExtract %4 %80 1
+ %91 = OpFMul %4 %90 %82
+ %92 = OpFAdd %4 %89 %91
+ %93 = OpCompositeExtract %4 %79 2
+ %94 = OpFMul %4 %93 %81
+ %95 = OpCompositeExtract %4 %80 2
+ %96 = OpFMul %4 %95 %82
+ %97 = OpFAdd %4 %94 %96
+ %57 = OpCompositeConstruct %6 %87 %92 %97
+
+; Multiplying 4x2 matrix by 2-dimensional vector
+ %98 = OpCompositeExtract %7 %51 0
+ %99 = OpCompositeExtract %7 %51 1
+ %100 = OpCompositeExtract %4 %35 0
+ %101 = OpCompositeExtract %4 %35 1
+ %102 = OpCompositeExtract %4 %98 0
+ %103 = OpFMul %4 %102 %100
+ %104 = OpCompositeExtract %4 %99 0
+ %105 = OpFMul %4 %104 %101
+ %106 = OpFAdd %4 %103 %105
+ %107 = OpCompositeExtract %4 %98 1
+ %108 = OpFMul %4 %107 %100
+ %109 = OpCompositeExtract %4 %99 1
+ %110 = OpFMul %4 %109 %101
+ %111 = OpFAdd %4 %108 %110
+ %112 = OpCompositeExtract %4 %98 2
+ %113 = OpFMul %4 %112 %100
+ %114 = OpCompositeExtract %4 %99 2
+ %115 = OpFMul %4 %114 %101
+ %116 = OpFAdd %4 %113 %115
+ %117 = OpCompositeExtract %4 %98 3
+ %118 = OpFMul %4 %117 %100
+ %119 = OpCompositeExtract %4 %99 3
+ %120 = OpFMul %4 %119 %101
+ %121 = OpFAdd %4 %118 %120
+ %58 = OpCompositeConstruct %7 %106 %111 %116 %121
+
+; Multiplying 2x3 matrix by 3-dimensional vector
+ %122 = OpCompositeExtract %5 %46 0
+ %123 = OpCompositeExtract %5 %46 1
+ %124 = OpCompositeExtract %5 %46 2
+ %125 = OpCompositeExtract %4 %37 0
+ %126 = OpCompositeExtract %4 %37 1
+ %127 = OpCompositeExtract %4 %37 2
+ %128 = OpCompositeExtract %4 %122 0
+ %129 = OpFMul %4 %128 %125
+ %130 = OpCompositeExtract %4 %123 0
+ %131 = OpFMul %4 %130 %126
+ %132 = OpCompositeExtract %4 %124 0
+ %133 = OpFMul %4 %132 %127
+ %134 = OpFAdd %4 %129 %131
+ %135 = OpFAdd %4 %133 %134
+ %136 = OpCompositeExtract %4 %122 1
+ %137 = OpFMul %4 %136 %125
+ %138 = OpCompositeExtract %4 %123 1
+ %139 = OpFMul %4 %138 %126
+ %140 = OpCompositeExtract %4 %124 1
+ %141 = OpFMul %4 %140 %127
+ %142 = OpFAdd %4 %137 %139
+ %143 = OpFAdd %4 %141 %142
+ %59 = OpCompositeConstruct %5 %135 %143
+
+; Multiplying 3x3 matrix by 3-dimensional vector
+ %60 = OpMatrixTimesVector %6 %49 %38
+
+; Multiplying 4x3 matrix by 3-dimensional vector
+ %61 = OpMatrixTimesVector %7 %52 %39
+
+; Multiplying 2x4 matrix by 4-dimensional vector
+ %62 = OpMatrixTimesVector %5 %47 %41
+
+; Multiplying 3x4 matrix by 4-dimensional vector
+ %63 = OpMatrixTimesVector %6 %50 %42
+
+; Multiplying 4x4 matrix by 4-dimensional vector
+ %64 = OpMatrixTimesVector %7 %53 %43
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest,
+ ReplaceOpMatrixTimesMatrix) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2x2 matrix
+ %56 = OpMatrixTimesMatrix %8 %45 %45
+
+; Multiplying 2x2 matrix by 2x3 matrix
+ %57 = OpMatrixTimesMatrix %9 %45 %46
+
+; Multiplying 2x2 matrix by 2x4 matrix
+ %58 = OpMatrixTimesMatrix %10 %45 %47
+
+; Multiplying 2x3 matrix by 3x2 matrix
+ %59 = OpMatrixTimesMatrix %8 %46 %48
+
+; Multiplying 2x3 matrix by 3x3 matrix
+ %60 = OpMatrixTimesMatrix %9 %46 %49
+
+; Multiplying 2x3 matrix by 3x4 matrix
+ %61 = OpMatrixTimesMatrix %10 %46 %50
+
+; Multiplying 2x4 matrix by 4x2 matrix
+ %62 = OpMatrixTimesMatrix %8 %47 %51
+
+; Multiplying 2x4 matrix by 4x3 matrix
+ %63 = OpMatrixTimesMatrix %9 %47 %52
+
+; Multiplying 2x4 matrix by 4x4 matrix
+ %64 = OpMatrixTimesMatrix %10 %47 %53
+
+; Multiplying 3x2 matrix by 2x2 matrix
+ %65 = OpMatrixTimesMatrix %11 %48 %45
+
+; Multiplying 3x2 matrix by 2x3 matrix
+ %66 = OpMatrixTimesMatrix %12 %48 %46
+
+; Multiplying 3x2 matrix by 2x4 matrix
+ %67 = OpMatrixTimesMatrix %13 %48 %47
+
+; Multiplying 3x3 matrix by 3x2 matrix
+ %68 = OpMatrixTimesMatrix %11 %49 %48
+
+; Multiplying 3x3 matrix by 3x3 matrix
+ %69 = OpMatrixTimesMatrix %12 %49 %49
+
+; Multiplying 3x3 matrix by 3x4 matrix
+ %70 = OpMatrixTimesMatrix %13 %49 %50
+
+; Multiplying 3x4 matrix by 4x2 matrix
+ %71 = OpMatrixTimesMatrix %11 %50 %51
+
+; Multiplying 3x4 matrix by 4x3 matrix
+ %72 = OpMatrixTimesMatrix %12 %50 %52
+
+; Multiplying 3x4 matrix by 4x4 matrix
+ %73 = OpMatrixTimesMatrix %13 %50 %53
+
+; Multiplying 4x2 matrix by 2x2 matrix
+ %74 = OpMatrixTimesMatrix %14 %51 %45
+
+; Multiplying 4x2 matrix by 2x3 matrix
+ %75 = OpMatrixTimesMatrix %15 %51 %46
+
+; Multiplying 4x2 matrix by 2x4 matrix
+ %76 = OpMatrixTimesMatrix %16 %51 %47
+
+; Multiplying 4x3 matrix by 3x2 matrix
+ %77 = OpMatrixTimesMatrix %14 %52 %48
+
+; Multiplying 4x3 matrix by 3x3 matrix
+ %78 = OpMatrixTimesMatrix %15 %52 %49
+
+; Multiplying 4x3 matrix by 3x4 matrix
+ %79 = OpMatrixTimesMatrix %16 %52 %50
+
+; Multiplying 4x4 matrix by 4x2 matrix
+ %80 = OpMatrixTimesMatrix %14 %53 %51
+
+; Multiplying 4x4 matrix by 4x3 matrix
+ %81 = OpMatrixTimesMatrix %15 %53 %52
+
+; Multiplying 4x4 matrix by 4x4 matrix
+ %82 = OpMatrixTimesMatrix %16 %53 %53
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(56, SpvOpMatrixTimesMatrix, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(57, SpvOpMatrixTimesMatrix, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+ 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(58, SpvOpMatrixTimesMatrix, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
+ 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %54 "main"
+ OpExecutionMode %54 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %54 "main"
+
+; Types
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeMatrix %5 2
+ %9 = OpTypeMatrix %5 3
+ %10 = OpTypeMatrix %5 4
+ %11 = OpTypeMatrix %6 2
+ %12 = OpTypeMatrix %6 3
+ %13 = OpTypeMatrix %6 4
+ %14 = OpTypeMatrix %7 2
+ %15 = OpTypeMatrix %7 3
+ %16 = OpTypeMatrix %7 4
+
+; Constant scalars
+ %17 = OpConstant %4 1
+ %18 = OpConstant %4 2
+ %19 = OpConstant %4 3
+ %20 = OpConstant %4 4
+ %21 = OpConstant %4 5
+ %22 = OpConstant %4 6
+ %23 = OpConstant %4 7
+ %24 = OpConstant %4 8
+ %25 = OpConstant %4 9
+ %26 = OpConstant %4 10
+ %27 = OpConstant %4 11
+ %28 = OpConstant %4 12
+ %29 = OpConstant %4 13
+ %30 = OpConstant %4 14
+ %31 = OpConstant %4 15
+ %32 = OpConstant %4 16
+
+; Constant vectors
+ %33 = OpConstantComposite %5 %17 %18
+ %34 = OpConstantComposite %5 %19 %20
+ %35 = OpConstantComposite %5 %21 %22
+ %36 = OpConstantComposite %5 %23 %24
+ %37 = OpConstantComposite %6 %17 %18 %19
+ %38 = OpConstantComposite %6 %20 %21 %22
+ %39 = OpConstantComposite %6 %23 %24 %25
+ %40 = OpConstantComposite %6 %26 %27 %28
+ %41 = OpConstantComposite %7 %17 %18 %19 %20
+ %42 = OpConstantComposite %7 %21 %22 %23 %24
+ %43 = OpConstantComposite %7 %25 %26 %27 %28
+ %44 = OpConstantComposite %7 %29 %30 %31 %32
+
+; Constant matrices
+ %45 = OpConstantComposite %8 %33 %34
+ %46 = OpConstantComposite %9 %33 %34 %35
+ %47 = OpConstantComposite %10 %33 %34 %35 %36
+ %48 = OpConstantComposite %11 %37 %38
+ %49 = OpConstantComposite %12 %37 %38 %39
+ %50 = OpConstantComposite %13 %37 %38 %39 %40
+ %51 = OpConstantComposite %14 %41 %42
+ %52 = OpConstantComposite %15 %41 %42 %43
+ %53 = OpConstantComposite %16 %41 %42 %43 %44
+
+; main function
+ %54 = OpFunction %2 None %3
+ %55 = OpLabel
+
+; Multiplying 2x2 matrix by 2x2 matrix
+ %83 = OpCompositeExtract %5 %45 0 ; matrix 2 column 0
+ %84 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %85 = OpCompositeExtract %4 %84 0 ; matrix 1 row 0 column 0
+ %86 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0
+ %87 = OpFMul %4 %85 %86
+ %88 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %89 = OpCompositeExtract %4 %88 0 ; matrix 1 row 0 column 1
+ %90 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0
+ %91 = OpFMul %4 %89 %90
+ %92 = OpFAdd %4 %87 %91
+ %93 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %94 = OpCompositeExtract %4 %93 1 ; matrix 1 row 1 column 0
+ %95 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0
+ %96 = OpFMul %4 %94 %95
+ %97 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %98 = OpCompositeExtract %4 %97 1 ; matrix 1 row 1 column 1
+ %99 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0
+ %100 = OpFMul %4 %98 %99
+ %101 = OpFAdd %4 %96 %100
+ %102 = OpCompositeConstruct %5 %92 %101 ; resulting matrix column 0
+ %103 = OpCompositeExtract %5 %45 1 ; matrix 2 column 1
+ %104 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %105 = OpCompositeExtract %4 %104 0 ; matrix 1 row 0 column 0
+ %106 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1
+ %107 = OpFMul %4 %105 %106
+ %108 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %109 = OpCompositeExtract %4 %108 0 ; matrix 1 row 0 column 1
+ %110 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1
+ %111 = OpFMul %4 %109 %110
+ %112 = OpFAdd %4 %107 %111
+ %113 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %114 = OpCompositeExtract %4 %113 1 ; matrix 1 row 1 column 0
+ %115 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1
+ %116 = OpFMul %4 %114 %115
+ %117 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %118 = OpCompositeExtract %4 %117 1 ; matrix 1 row 1 column 1
+ %119 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1
+ %120 = OpFMul %4 %118 %119
+ %121 = OpFAdd %4 %116 %120
+ %122 = OpCompositeConstruct %5 %112 %121 ; resulting matrix column 1
+ %56 = OpCompositeConstruct %8 %102 %122 ; resulting matrix
+
+; Multiplying 2x2 matrix by 2x3 matrix
+ %123 = OpCompositeExtract %5 %46 0 ; matrix 2 column 0
+ %124 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %125 = OpCompositeExtract %4 %124 0 ; matrix 1 row 0 column 0
+ %126 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0
+ %127 = OpFMul %4 %125 %126
+ %128 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %129 = OpCompositeExtract %4 %128 0 ; matrix 1 row 0 column 1
+ %130 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0
+ %131 = OpFMul %4 %129 %130
+ %132 = OpFAdd %4 %127 %131
+ %133 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %134 = OpCompositeExtract %4 %133 1 ; matrix 1 row 1 column 0
+ %135 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0
+ %136 = OpFMul %4 %134 %135
+ %137 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %138 = OpCompositeExtract %4 %137 1 ; matrix 1 row 1 column 1
+ %139 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0
+ %140 = OpFMul %4 %138 %139
+ %141 = OpFAdd %4 %136 %140
+ %142 = OpCompositeConstruct %5 %132 %141 ; resulting matrix column 0
+ %143 = OpCompositeExtract %5 %46 1 ; matrix 2 column 1
+ %144 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %145 = OpCompositeExtract %4 %144 0 ; matrix 1 row 0 column 0
+ %146 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1
+ %147 = OpFMul %4 %145 %146
+ %148 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %149 = OpCompositeExtract %4 %148 0 ; matrix 1 row 0 column 1
+ %150 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1
+ %151 = OpFMul %4 %149 %150
+ %152 = OpFAdd %4 %147 %151
+ %153 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %154 = OpCompositeExtract %4 %153 1 ; matrix 1 row 1 column 0
+ %155 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1
+ %156 = OpFMul %4 %154 %155
+ %157 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %158 = OpCompositeExtract %4 %157 1 ; matrix 1 row 1 column 1
+ %159 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1
+ %160 = OpFMul %4 %158 %159
+ %161 = OpFAdd %4 %156 %160
+ %162 = OpCompositeConstruct %5 %152 %161 ; resulting matrix column 1
+ %163 = OpCompositeExtract %5 %46 2 ; matrix 2 column 2
+ %164 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %165 = OpCompositeExtract %4 %164 0 ; matrix 1 row 0 column 0
+ %166 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2
+ %167 = OpFMul %4 %165 %166
+ %168 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %169 = OpCompositeExtract %4 %168 0 ; matrix 1 row 0 column 1
+ %170 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2
+ %171 = OpFMul %4 %169 %170
+ %172 = OpFAdd %4 %167 %171
+ %173 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %174 = OpCompositeExtract %4 %173 1 ; matrix 1 row 1 column 0
+ %175 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2
+ %176 = OpFMul %4 %174 %175
+ %177 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %178 = OpCompositeExtract %4 %177 1 ; matrix 1 row 1 column 1
+ %179 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2
+ %180 = OpFMul %4 %178 %179
+ %181 = OpFAdd %4 %176 %180
+ %182 = OpCompositeConstruct %5 %172 %181 ; resulting matrix column 2
+ %57 = OpCompositeConstruct %9 %142 %162 %182
+
+; Multiplying 2x2 matrix by 2x4 matrix
+ %183 = OpCompositeExtract %5 %47 0 ; matrix 2 column 0
+ %184 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %185 = OpCompositeExtract %4 %184 0 ; matrix 1 row 0 column 0
+ %186 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0
+ %187 = OpFMul %4 %185 %186
+ %188 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %189 = OpCompositeExtract %4 %188 0 ; matrix 1 row 0 column 1
+ %190 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0
+ %191 = OpFMul %4 %189 %190
+ %192 = OpFAdd %4 %187 %191
+ %193 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %194 = OpCompositeExtract %4 %193 1 ; matrix 1 row 1 column 0
+ %195 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0
+ %196 = OpFMul %4 %194 %195
+ %197 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %198 = OpCompositeExtract %4 %197 1 ; matrix 1 row 1 column 1
+ %199 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0
+ %200 = OpFMul %4 %198 %199
+ %201 = OpFAdd %4 %196 %200
+ %202 = OpCompositeConstruct %5 %192 %201 ; resulting matrix column 0
+ %203 = OpCompositeExtract %5 %47 1 ; matrix 2 column 1
+ %204 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %205 = OpCompositeExtract %4 %204 0 ; matrix 1 row 0 column 0
+ %206 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1
+ %207 = OpFMul %4 %205 %206
+ %208 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %209 = OpCompositeExtract %4 %208 0 ; matrix 1 row 0 column 1
+ %210 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1
+ %211 = OpFMul %4 %209 %210
+ %212 = OpFAdd %4 %207 %211
+ %213 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %214 = OpCompositeExtract %4 %213 1 ; matrix 1 row 1 column 0
+ %215 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1
+ %216 = OpFMul %4 %214 %215
+ %217 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %218 = OpCompositeExtract %4 %217 1 ; matrix 1 row 1 column 1
+ %219 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1
+ %220 = OpFMul %4 %218 %219
+ %221 = OpFAdd %4 %216 %220
+ %222 = OpCompositeConstruct %5 %212 %221 ; resulting matrix column 1
+ %223 = OpCompositeExtract %5 %47 2 ; matrix 2 column 2
+ %224 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %225 = OpCompositeExtract %4 %224 0 ; matrix 1 row 0 column 0
+ %226 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2
+ %227 = OpFMul %4 %225 %226
+ %228 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %229 = OpCompositeExtract %4 %228 0 ; matrix 1 row 0 column 1
+ %230 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2
+ %231 = OpFMul %4 %229 %230
+ %232 = OpFAdd %4 %227 %231
+ %233 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %234 = OpCompositeExtract %4 %233 1 ; matrix 1 row 1 column 0
+ %235 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2
+ %236 = OpFMul %4 %234 %235
+ %237 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %238 = OpCompositeExtract %4 %237 1 ; matrix 1 row 1 column 1
+ %239 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2
+ %240 = OpFMul %4 %238 %239
+ %241 = OpFAdd %4 %236 %240
+ %242 = OpCompositeConstruct %5 %232 %241 ; resulting matrix column 2
+ %243 = OpCompositeExtract %5 %47 3 ; matrix 2 column 3
+ %244 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %245 = OpCompositeExtract %4 %244 0 ; matrix 1 row 0 column 0
+ %246 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3
+ %247 = OpFMul %4 %245 %246
+ %248 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %249 = OpCompositeExtract %4 %248 0 ; matrix 1 row 0 column 1
+ %250 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3
+ %251 = OpFMul %4 %249 %250
+ %252 = OpFAdd %4 %247 %251
+ %253 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0
+ %254 = OpCompositeExtract %4 %253 1 ; matrix 1 row 1 column 0
+ %255 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3
+ %256 = OpFMul %4 %254 %255
+ %257 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1
+ %258 = OpCompositeExtract %4 %257 1 ; matrix 1 row 1 column 1
+ %259 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3
+ %260 = OpFMul %4 %258 %259
+ %261 = OpFAdd %4 %256 %260
+ %262 = OpCompositeConstruct %5 %252 %261 ; resulting matrix column 3
+ %58 = OpCompositeConstruct %10 %202 %222 %242 %262
+
+; Multiplying 2x3 matrix by 3x2 matrix
+ %59 = OpMatrixTimesMatrix %8 %46 %48
+
+; Multiplying 2x3 matrix by 3x3 matrix
+ %60 = OpMatrixTimesMatrix %9 %46 %49
+
+; Multiplying 2x3 matrix by 3x4 matrix
+ %61 = OpMatrixTimesMatrix %10 %46 %50
+
+; Multiplying 2x4 matrix by 4x2 matrix
+ %62 = OpMatrixTimesMatrix %8 %47 %51
+
+; Multiplying 2x4 matrix by 4x3 matrix
+ %63 = OpMatrixTimesMatrix %9 %47 %52
+
+; Multiplying 2x4 matrix by 4x4 matrix
+ %64 = OpMatrixTimesMatrix %10 %47 %53
+
+; Multiplying 3x2 matrix by 2x2 matrix
+ %65 = OpMatrixTimesMatrix %11 %48 %45
+
+; Multiplying 3x2 matrix by 2x3 matrix
+ %66 = OpMatrixTimesMatrix %12 %48 %46
+
+; Multiplying 3x2 matrix by 2x4 matrix
+ %67 = OpMatrixTimesMatrix %13 %48 %47
+
+; Multiplying 3x3 matrix by 3x2 matrix
+ %68 = OpMatrixTimesMatrix %11 %49 %48
+
+; Multiplying 3x3 matrix by 3x3 matrix
+ %69 = OpMatrixTimesMatrix %12 %49 %49
+
+; Multiplying 3x3 matrix by 3x4 matrix
+ %70 = OpMatrixTimesMatrix %13 %49 %50
+
+; Multiplying 3x4 matrix by 4x2 matrix
+ %71 = OpMatrixTimesMatrix %11 %50 %51
+
+; Multiplying 3x4 matrix by 4x3 matrix
+ %72 = OpMatrixTimesMatrix %12 %50 %52
+
+; Multiplying 3x4 matrix by 4x4 matrix
+ %73 = OpMatrixTimesMatrix %13 %50 %53
+
+; Multiplying 4x2 matrix by 2x2 matrix
+ %74 = OpMatrixTimesMatrix %14 %51 %45
+
+; Multiplying 4x2 matrix by 2x3 matrix
+ %75 = OpMatrixTimesMatrix %15 %51 %46
+
+; Multiplying 4x2 matrix by 2x4 matrix
+ %76 = OpMatrixTimesMatrix %16 %51 %47
+
+; Multiplying 4x3 matrix by 3x2 matrix
+ %77 = OpMatrixTimesMatrix %14 %52 %48
+
+; Multiplying 4x3 matrix by 3x3 matrix
+ %78 = OpMatrixTimesMatrix %15 %52 %49
+
+; Multiplying 4x3 matrix by 3x4 matrix
+ %79 = OpMatrixTimesMatrix %16 %52 %50
+
+; Multiplying 4x4 matrix by 4x2 matrix
+ %80 = OpMatrixTimesMatrix %14 %53 %51
+
+; Multiplying 4x4 matrix by 4x3 matrix
+ %81 = OpMatrixTimesMatrix %15 %53 %52
+
+; Multiplying 4x4 matrix by 4x4 matrix
+ %82 = OpMatrixTimesMatrix %16 %53 %53
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+TEST(TransformationReplaceLinearAlgebraInstructionTest, ReplaceOpDot) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %22 "main"
+ OpExecutionMode %22 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %22 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpConstant %4 1
+ %9 = OpConstant %4 2
+ %10 = OpConstant %4 3
+ %11 = OpConstant %4 4
+ %12 = OpConstant %4 5
+ %13 = OpConstant %4 6
+ %14 = OpConstant %4 7
+ %15 = OpConstant %4 8
+ %16 = OpConstantComposite %5 %8 %9
+ %17 = OpConstantComposite %5 %10 %11
+ %18 = OpConstantComposite %6 %8 %9 %10
+ %19 = OpConstantComposite %6 %11 %12 %13
+ %20 = OpConstantComposite %7 %8 %9 %10 %11
+ %21 = OpConstantComposite %7 %12 %13 %14 %15
+ %22 = OpFunction %2 None %3
+ %23 = OpLabel
+ %24 = OpDot %4 %16 %17
+ %25 = OpDot %4 %18 %19
+ %26 = OpDot %4 %20 %21
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0);
+ auto transformation = TransformationReplaceLinearAlgebraInstruction(
+ {27, 28, 29, 30, 31, 32}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {33, 34, 35, 36, 37, 38, 39, 40, 41, 42}, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0);
+ transformation = TransformationReplaceLinearAlgebraInstruction(
+ {43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56},
+ instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %22 "main"
+ OpExecutionMode %22 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %22 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpConstant %4 1
+ %9 = OpConstant %4 2
+ %10 = OpConstant %4 3
+ %11 = OpConstant %4 4
+ %12 = OpConstant %4 5
+ %13 = OpConstant %4 6
+ %14 = OpConstant %4 7
+ %15 = OpConstant %4 8
+ %16 = OpConstantComposite %5 %8 %9
+ %17 = OpConstantComposite %5 %10 %11
+ %18 = OpConstantComposite %6 %8 %9 %10
+ %19 = OpConstantComposite %6 %11 %12 %13
+ %20 = OpConstantComposite %7 %8 %9 %10 %11
+ %21 = OpConstantComposite %7 %12 %13 %14 %15
+ %22 = OpFunction %2 None %3
+ %23 = OpLabel
+ %27 = OpCompositeExtract %4 %16 0
+ %28 = OpCompositeExtract %4 %17 0
+ %29 = OpFMul %4 %27 %28
+ %30 = OpCompositeExtract %4 %16 1
+ %31 = OpCompositeExtract %4 %17 1
+ %32 = OpFMul %4 %30 %31
+ %24 = OpFAdd %4 %29 %32
+ %33 = OpCompositeExtract %4 %18 0
+ %34 = OpCompositeExtract %4 %19 0
+ %35 = OpFMul %4 %33 %34
+ %36 = OpCompositeExtract %4 %18 1
+ %37 = OpCompositeExtract %4 %19 1
+ %38 = OpFMul %4 %36 %37
+ %39 = OpCompositeExtract %4 %18 2
+ %40 = OpCompositeExtract %4 %19 2
+ %41 = OpFMul %4 %39 %40
+ %42 = OpFAdd %4 %35 %38
+ %25 = OpFAdd %4 %41 %42
+ %43 = OpCompositeExtract %4 %20 0
+ %44 = OpCompositeExtract %4 %21 0
+ %45 = OpFMul %4 %43 %44
+ %46 = OpCompositeExtract %4 %20 1
+ %47 = OpCompositeExtract %4 %21 1
+ %48 = OpFMul %4 %46 %47
+ %49 = OpCompositeExtract %4 %20 2
+ %50 = OpCompositeExtract %4 %21 2
+ %51 = OpFMul %4 %49 %50
+ %52 = OpCompositeExtract %4 %20 3
+ %53 = OpCompositeExtract %4 %21 3
+ %54 = OpFMul %4 %52 %53
+ %55 = OpFAdd %4 %45 %48
+ %56 = OpFAdd %4 %51 %55
+ %26 = OpFAdd %4 %54 %56
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
new file mode 100644
index 0000000..aa3d1fc
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp
@@ -0,0 +1,197 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceLoadStoreWithCopyMemoryTest, BasicScenarios) {
+ // This is a simple transformation and this test handles the main cases.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %12 "c"
+ OpName %14 "d"
+ OpName %18 "e"
+ OpName %20 "f"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %13 = OpConstant %6 4
+ %15 = OpConstant %6 5
+ %16 = OpTypeFloat 32
+ %17 = OpTypePointer Function %16
+ %19 = OpConstant %16 2
+ %21 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ %14 = OpVariable %7 Function
+ %18 = OpVariable %17 Function
+ %20 = OpVariable %17 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %12 %13
+ OpStore %14 %15
+ OpStore %18 %19
+ OpStore %20 %21
+ %22 = OpLoad %6 %8
+ OpCopyMemory %10 %8
+ OpStore %10 %22
+ %23 = OpLoad %6 %12
+ OpStore %14 %23
+ %24 = OpLoad %16 %18
+ OpStore %20 %24
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto bad_instruction_descriptor_1 =
+ MakeInstructionDescriptor(5, SpvOpVariable, 0);
+
+ auto load_instruction_descriptor_1 =
+ MakeInstructionDescriptor(5, SpvOpLoad, 0);
+ auto load_instruction_descriptor_2 =
+ MakeInstructionDescriptor(5, SpvOpLoad, 1);
+ auto load_instruction_descriptor_3 =
+ MakeInstructionDescriptor(5, SpvOpLoad, 2);
+ auto store_instruction_descriptor_1 =
+ MakeInstructionDescriptor(22, SpvOpStore, 0);
+ auto store_instruction_descriptor_2 =
+ MakeInstructionDescriptor(23, SpvOpStore, 0);
+ auto store_instruction_descriptor_3 =
+ MakeInstructionDescriptor(24, SpvOpStore, 0);
+
+ // Bad: |load_instruction_descriptor| is incorrect.
+ auto transformation_bad_1 = TransformationReplaceLoadStoreWithCopyMemory(
+ bad_instruction_descriptor_1, store_instruction_descriptor_1);
+ ASSERT_FALSE(
+ transformation_bad_1.IsApplicable(context.get(), transformation_context));
+
+ // Bad: |store_instruction_descriptor| is incorrect.
+ auto transformation_bad_2 = TransformationReplaceLoadStoreWithCopyMemory(
+ load_instruction_descriptor_1, bad_instruction_descriptor_1);
+ ASSERT_FALSE(
+ transformation_bad_2.IsApplicable(context.get(), transformation_context));
+
+ // Bad: Intermediate values of the OpLoad and the OpStore don't match.
+ auto transformation_bad_3 = TransformationReplaceLoadStoreWithCopyMemory(
+ load_instruction_descriptor_1, store_instruction_descriptor_2);
+ ASSERT_FALSE(
+ transformation_bad_3.IsApplicable(context.get(), transformation_context));
+
+ // Bad: There is a interfering OpCopyMemory instruction between the OpLoad and
+ // the OpStore.
+ auto transformation_bad_4 = TransformationReplaceLoadStoreWithCopyMemory(
+ load_instruction_descriptor_1, store_instruction_descriptor_1);
+ ASSERT_FALSE(
+ transformation_bad_4.IsApplicable(context.get(), transformation_context));
+
+ auto transformation_good_1 = TransformationReplaceLoadStoreWithCopyMemory(
+ load_instruction_descriptor_2, store_instruction_descriptor_2);
+ ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_1.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation_good_2 = TransformationReplaceLoadStoreWithCopyMemory(
+ load_instruction_descriptor_3, store_instruction_descriptor_3);
+ ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
+ transformation_context));
+ transformation_good_2.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformations = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "a"
+ OpName %10 "b"
+ OpName %12 "c"
+ OpName %14 "d"
+ OpName %18 "e"
+ OpName %20 "f"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 2
+ %11 = OpConstant %6 3
+ %13 = OpConstant %6 4
+ %15 = OpConstant %6 5
+ %16 = OpTypeFloat 32
+ %17 = OpTypePointer Function %16
+ %19 = OpConstant %16 2
+ %21 = OpConstant %16 3
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ %12 = OpVariable %7 Function
+ %14 = OpVariable %7 Function
+ %18 = OpVariable %17 Function
+ %20 = OpVariable %17 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ OpStore %12 %13
+ OpStore %14 %15
+ OpStore %18 %19
+ OpStore %20 %21
+ %22 = OpLoad %6 %8
+ OpCopyMemory %10 %8
+ OpStore %10 %22
+ %23 = OpLoad %6 %12
+ OpCopyMemory %14 %12
+ %24 = OpLoad %16 %18
+ OpCopyMemory %20 %18
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp
new file mode 100644
index 0000000..25e25db
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp
@@ -0,0 +1,353 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_parameter_with_global.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpMemberDecorate %13 0 RelaxedPrecision
+ OpDecorate %16 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Private %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Private %8
+ %10 = OpTypeVector %8 2
+ %11 = OpTypePointer Private %10
+ %12 = OpTypeBool
+ %71 = OpTypeFunction %2 %6
+ %83 = OpTypeFunction %2 %6 %12
+ %93 = OpTypeFunction %2 %10
+ %94 = OpTypeFunction %2 %8 %10
+ %40 = OpTypePointer Function %12
+ %13 = OpTypeStruct %6 %8
+ %14 = OpTypePointer Private %13
+ %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
+ %22 = OpConstant %6 0
+ %23 = OpConstant %8 0
+ %26 = OpConstantComposite %10 %23 %23
+ %27 = OpConstantTrue %12
+ %28 = OpConstantComposite %13 %22 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %41 = OpVariable %40 Function %27
+ %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
+ OpReturn
+ OpFunctionEnd
+
+ ; adjust type of the function in-place
+ %20 = OpFunction %2 None %15
+ %16 = OpFunctionParameter %6
+ %17 = OpFunctionParameter %8
+ %18 = OpFunctionParameter %10
+ %19 = OpFunctionParameter %13
+ %42 = OpFunctionParameter %40
+ %43 = OpFunctionParameter %12
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; reuse an existing function type
+ %70 = OpFunction %2 None %71
+ %72 = OpFunctionParameter %6
+ %73 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %74 = OpFunction %2 None %71
+ %75 = OpFunctionParameter %6
+ %76 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; create a new function type
+ %77 = OpFunction %2 None %83
+ %78 = OpFunctionParameter %6
+ %84 = OpFunctionParameter %12
+ %79 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %80 = OpFunction %2 None %83
+ %81 = OpFunctionParameter %6
+ %85 = OpFunctionParameter %12
+ %82 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; don't adjust the type of the function if it creates a duplicate
+ %86 = OpFunction %2 None %93
+ %87 = OpFunctionParameter %10
+ %89 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %90 = OpFunction %2 None %94
+ %91 = OpFunctionParameter %8
+ %95 = OpFunctionParameter %10
+ %92 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Parameter id is invalid.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 50, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter id is not a result id of an OpFunctionParameter instruction.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 21, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter has unsupported type.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 42, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Initializer for a global variable doesn't exist in the module.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Pointer type for a global variable doesn't exist in the module.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Function type id is not fresh.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(16, 16, 51)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Global variable id is not fresh.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 16)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Function type fresh id and global variable fresh id are equal.
+ ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 50)
+ .IsApplicable(context.get(), transformation_context));
+
+ {
+ TransformationReplaceParameterWithGlobal transformation(50, 16, 51);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(52, 17, 53);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(54, 18, 55);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(56, 19, 57);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(58, 75, 59);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(60, 81, 61);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(62, 91, 63);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpMemberDecorate %13 0 RelaxedPrecision
+ OpDecorate %16 RelaxedPrecision
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Private %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Private %8
+ %10 = OpTypeVector %8 2
+ %11 = OpTypePointer Private %10
+ %12 = OpTypeBool
+ %71 = OpTypeFunction %2 %6
+ %83 = OpTypeFunction %2 %6 %12
+ %93 = OpTypeFunction %2 %10
+ %40 = OpTypePointer Function %12
+ %13 = OpTypeStruct %6 %8
+ %14 = OpTypePointer Private %13
+ %22 = OpConstant %6 0
+ %23 = OpConstant %8 0
+ %26 = OpConstantComposite %10 %23 %23
+ %27 = OpConstantTrue %12
+ %28 = OpConstantComposite %13 %22 %23
+ %51 = OpVariable %7 Private %22
+ %53 = OpVariable %9 Private %23
+ %55 = OpVariable %11 Private %26
+ %57 = OpVariable %14 Private %28
+ %15 = OpTypeFunction %2 %40 %12
+ %59 = OpVariable %7 Private %22
+ %61 = OpVariable %7 Private %22
+ %60 = OpTypeFunction %2 %12
+ %63 = OpVariable %9 Private %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %41 = OpVariable %40 Function %27
+ OpStore %51 %22
+ OpStore %53 %23
+ OpStore %55 %26
+ OpStore %57 %28
+ %33 = OpFunctionCall %2 %20 %41 %27
+ OpReturn
+ OpFunctionEnd
+ %20 = OpFunction %2 None %15
+ %42 = OpFunctionParameter %40
+ %43 = OpFunctionParameter %12
+ %21 = OpLabel
+ %19 = OpLoad %13 %57
+ %18 = OpLoad %10 %55
+ %17 = OpLoad %8 %53
+ %16 = OpLoad %6 %51
+ OpReturn
+ OpFunctionEnd
+ %70 = OpFunction %2 None %71
+ %72 = OpFunctionParameter %6
+ %73 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %74 = OpFunction %2 None %3
+ %76 = OpLabel
+ %75 = OpLoad %6 %59
+ OpReturn
+ OpFunctionEnd
+ %77 = OpFunction %2 None %83
+ %78 = OpFunctionParameter %6
+ %84 = OpFunctionParameter %12
+ %79 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %80 = OpFunction %2 None %60
+ %85 = OpFunctionParameter %12
+ %82 = OpLabel
+ %81 = OpLoad %6 %61
+ OpReturn
+ OpFunctionEnd
+ %86 = OpFunction %2 None %93
+ %87 = OpFunctionParameter %10
+ %89 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %90 = OpFunction %2 None %93
+ %95 = OpFunctionParameter %10
+ %92 = OpLabel
+ %91 = OpLoad %8 %63
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+TEST(TransformationReplaceParameterWithGlobalTest,
+ HandlesIrrelevantParameters) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %9 = OpTypeInt 32 1
+ %3 = OpTypeFunction %2
+ %7 = OpTypeFunction %2 %9 %9
+ %12 = OpTypePointer Private %9
+ %13 = OpConstant %9 0
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %7
+ %10 = OpFunctionParameter %9
+ %11 = OpFunctionParameter %9
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactIdIsIrrelevant(10);
+
+ {
+ TransformationReplaceParameterWithGlobal transformation(20, 10, 21);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(fact_manager.PointeeValueIsIrrelevant(21));
+ }
+ {
+ TransformationReplaceParameterWithGlobal transformation(22, 11, 23);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_FALSE(fact_manager.PointeeValueIsIrrelevant(23));
+ }
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp
new file mode 100644
index 0000000..e59f6ea
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_replace_params_with_struct_test.cpp
@@ -0,0 +1,338 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationReplaceParamsWithStructTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpMemberDecorate %13 0 RelaxedPrecision
+ OpDecorate %16 RelaxedPrecision
+ %2 = OpTypeVoid
+ %6 = OpTypeInt 32 1
+ %3 = OpTypeFunction %2
+ %7 = OpTypePointer Private %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Private %8
+ %10 = OpTypeVector %8 2
+ %11 = OpTypePointer Private %10
+ %12 = OpTypeBool
+ %51 = OpTypeFunction %2 %12
+ %64 = OpTypeStruct %6
+ %63 = OpTypeFunction %2 %64
+ %65 = OpTypeFunction %2 %6
+ %75 = OpTypeStruct %8
+ %76 = OpTypeFunction %2 %75
+ %77 = OpTypeFunction %2 %8
+ %40 = OpTypePointer Function %12
+ %13 = OpTypeStruct %6 %8
+ %45 = OpTypeStruct %6 %10 %13
+ %46 = OpTypeStruct %12
+ %47 = OpTypeStruct %8 %45 %46
+ %14 = OpTypePointer Private %13
+ %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
+ %22 = OpConstant %6 0
+ %23 = OpConstant %8 0
+ %26 = OpConstantComposite %10 %23 %23
+ %27 = OpConstantTrue %12
+ %28 = OpConstantComposite %13 %22 %23
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %41 = OpVariable %40 Function %27
+ %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
+ OpReturn
+ OpFunctionEnd
+
+ ; adjust type of the function in-place
+ %20 = OpFunction %2 None %15
+ %16 = OpFunctionParameter %6
+ %17 = OpFunctionParameter %8
+ %18 = OpFunctionParameter %10
+ %19 = OpFunctionParameter %13
+ %42 = OpFunctionParameter %40
+ %43 = OpFunctionParameter %12
+ %21 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; create a new function type
+ %50 = OpFunction %2 None %51
+ %52 = OpFunctionParameter %12
+ %53 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %54 = OpFunction %2 None %51
+ %55 = OpFunctionParameter %12
+ %56 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; reuse an existing function type
+ %57 = OpFunction %2 None %63
+ %58 = OpFunctionParameter %64
+ %59 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %60 = OpFunction %2 None %65
+ %61 = OpFunctionParameter %6
+ %62 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %66 = OpFunction %2 None %65
+ %67 = OpFunctionParameter %6
+ %68 = OpLabel
+ OpReturn
+ OpFunctionEnd
+
+ ; don't adjust the type of the function if it creates a duplicate
+ %69 = OpFunction %2 None %76
+ %70 = OpFunctionParameter %75
+ %71 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %72 = OpFunction %2 None %77
+ %73 = OpFunctionParameter %8
+ %74 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // |parameter_id| is empty.
+ ASSERT_FALSE(
+ TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // |parameter_id| has duplicates.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // |parameter_id| has invalid values.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter's belong to different functions.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // Parameter has unsupported type.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // OpTypeStruct does not exist in the module.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // |caller_id_to_fresh_composite_id| misses values.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // All fresh ids must be unique.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+ {{33, 90}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+ {{33, 92}, {90, 92}})
+ .IsApplicable(context.get(), transformation_context));
+
+ // All 'fresh' ids must be fresh.
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
+ {{33, 33}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33,
+ {{33, 92}, {90, 93}})
+ .IsApplicable(context.get(), transformation_context));
+
+ {
+ TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91,
+ {{33, 92}, {90, 93}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParamsWithStruct transformation({43}, 93, 94,
+ {{33, 95}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97,
+ {{33, 98}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+ {
+ TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpMemberDecorate %13 0 RelaxedPrecision
+ OpDecorate %16 RelaxedPrecision
+ %2 = OpTypeVoid
+ %6 = OpTypeInt 32 1
+ %3 = OpTypeFunction %2
+ %7 = OpTypePointer Private %6
+ %8 = OpTypeFloat 32
+ %9 = OpTypePointer Private %8
+ %10 = OpTypeVector %8 2
+ %11 = OpTypePointer Private %10
+ %12 = OpTypeBool
+ %51 = OpTypeFunction %2 %12
+ %64 = OpTypeStruct %6
+ %63 = OpTypeFunction %2 %64
+ %65 = OpTypeFunction %2 %6
+ %75 = OpTypeStruct %8
+ %76 = OpTypeFunction %2 %75
+ %40 = OpTypePointer Function %12
+ %13 = OpTypeStruct %6 %8
+ %45 = OpTypeStruct %6 %10 %13
+ %46 = OpTypeStruct %12
+ %47 = OpTypeStruct %8 %45 %46
+ %14 = OpTypePointer Private %13
+ %22 = OpConstant %6 0
+ %23 = OpConstant %8 0
+ %26 = OpConstantComposite %10 %23 %23
+ %27 = OpConstantTrue %12
+ %28 = OpConstantComposite %13 %22 %23
+ %15 = OpTypeFunction %2 %40 %47
+ %99 = OpTypeFunction %2 %46
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %41 = OpVariable %40 Function %27
+ %92 = OpCompositeConstruct %45 %22 %26 %28
+ %95 = OpCompositeConstruct %46 %27
+ %98 = OpCompositeConstruct %47 %23 %92 %95
+ %33 = OpFunctionCall %2 %20 %41 %98
+ OpReturn
+ OpFunctionEnd
+ %20 = OpFunction %2 None %15
+ %42 = OpFunctionParameter %40
+ %97 = OpFunctionParameter %47
+ %21 = OpLabel
+ %94 = OpCompositeExtract %46 %97 2
+ %91 = OpCompositeExtract %45 %97 1
+ %17 = OpCompositeExtract %8 %97 0
+ %43 = OpCompositeExtract %12 %94 0
+ %19 = OpCompositeExtract %13 %91 2
+ %18 = OpCompositeExtract %10 %91 1
+ %16 = OpCompositeExtract %6 %91 0
+ OpReturn
+ OpFunctionEnd
+ %50 = OpFunction %2 None %51
+ %52 = OpFunctionParameter %12
+ %53 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %54 = OpFunction %2 None %99
+ %100 = OpFunctionParameter %46
+ %56 = OpLabel
+ %55 = OpCompositeExtract %12 %100 0
+ OpReturn
+ OpFunctionEnd
+ %57 = OpFunction %2 None %63
+ %58 = OpFunctionParameter %64
+ %59 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %60 = OpFunction %2 None %63
+ %102 = OpFunctionParameter %64
+ %62 = OpLabel
+ %61 = OpCompositeExtract %6 %102 0
+ OpReturn
+ OpFunctionEnd
+ %66 = OpFunction %2 None %65
+ %67 = OpFunctionParameter %6
+ %68 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %69 = OpFunction %2 None %76
+ %70 = OpFunctionParameter %75
+ %71 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %72 = OpFunction %2 None %76
+ %104 = OpFunctionParameter %75
+ %74 = OpLabel
+ %73 = OpCompositeExtract %8 %104 0
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index c02d8d4..518ce9d 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -75,6 +75,7 @@
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16
OpCopyMemory %133 %12 Volatile
+ OpCopyMemory %133 %12
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None
@@ -109,12 +110,14 @@
0)
.IsApplicable(context.get(), transformation_context));
- TransformationSetMemoryOperandsMask transformation1(
- MakeInstructionDescriptor(147, SpvOpLoad, 0),
- SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
- ASSERT_TRUE(
- transformation1.IsApplicable(context.get(), transformation_context));
- transformation1.Apply(context.get(), &transformation_context);
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(147, SpvOpLoad, 0),
+ SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
// Not OK to remove Aligned
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
@@ -128,15 +131,17 @@
SpvMemoryAccessAlignedMask, 0)
.IsApplicable(context.get(), transformation_context));
- // OK: adds Nontemporal and Volatile
- TransformationSetMemoryOperandsMask transformation2(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
- SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
- SpvMemoryAccessVolatileMask,
- 0);
- ASSERT_TRUE(
- transformation2.IsApplicable(context.get(), transformation_context));
- transformation2.Apply(context.get(), &transformation_context);
+ {
+ // OK: adds Nontemporal and Volatile
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+ SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
+ SpvMemoryAccessVolatileMask,
+ 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
// Not OK to remove Volatile
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
@@ -150,29 +155,45 @@
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
.IsApplicable(context.get(), transformation_context));
- // OK: adds Nontemporal
- TransformationSetMemoryOperandsMask transformation3(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
- SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
- ASSERT_TRUE(
- transformation3.IsApplicable(context.get(), transformation_context));
- transformation3.Apply(context.get(), &transformation_context);
+ {
+ // OK: adds Nontemporal
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- // OK: adds Nontemporal and Volatile
- TransformationSetMemoryOperandsMask transformation4(
- MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
- SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
- ASSERT_TRUE(
- transformation4.IsApplicable(context.get(), transformation_context));
- transformation4.Apply(context.get(), &transformation_context);
+ {
+ // OK: adds Nontemporal (creates new operand)
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- // OK: removes Nontemporal, adds Volatile
- TransformationSetMemoryOperandsMask transformation5(
- MakeInstructionDescriptor(148, SpvOpStore, 0),
- SpvMemoryAccessVolatileMask, 0);
- ASSERT_TRUE(
- transformation5.IsApplicable(context.get(), transformation_context));
- transformation5.Apply(context.get(), &transformation_context);
+ {
+ // OK: adds Nontemporal and Volatile
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ {
+ // OK: removes Nontemporal, adds Volatile
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(148, SpvOpStore, 0),
+ SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
std::string after_transformation = R"(
OpCapability Shader
@@ -228,6 +249,7 @@
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16
OpCopyMemory %133 %12 Nontemporal|Volatile
+ OpCopyMemory %133 %12 Nontemporal|Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 Nontemporal|Volatile
@@ -296,6 +318,8 @@
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
OpCopyMemory %133 %12 Volatile
+ OpCopyMemory %133 %12
+ OpCopyMemory %133 %12
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None Aligned 16
@@ -318,63 +342,95 @@
TransformationContext transformation_context(&fact_manager,
validator_options);
- TransformationSetMemoryOperandsMask transformation1(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
- SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
- // Bad: cannot remove aligned
- ASSERT_FALSE(TransformationSetMemoryOperandsMask(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
- SpvMemoryAccessVolatileMask, 1)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_TRUE(
- transformation1.IsApplicable(context.get(), transformation_context));
- transformation1.Apply(context.get(), &transformation_context);
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+ SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
+ // Bad: cannot remove aligned
+ ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
+ SpvMemoryAccessVolatileMask, 1)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- TransformationSetMemoryOperandsMask transformation2(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
- SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
- // Bad: cannot remove volatile
- ASSERT_FALSE(TransformationSetMemoryOperandsMask(
- MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
- SpvMemoryAccessNontemporalMask, 0)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_TRUE(
- transformation2.IsApplicable(context.get(), transformation_context));
- transformation2.Apply(context.get(), &transformation_context);
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+ // Bad: cannot remove volatile
+ ASSERT_FALSE(TransformationSetMemoryOperandsMask(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
+ SpvMemoryAccessNontemporalMask, 0)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- TransformationSetMemoryOperandsMask transformation3(
- MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
- SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
- // Bad: the first mask is None, so Aligned cannot be added to it.
- ASSERT_FALSE(TransformationSetMemoryOperandsMask(
- MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
- SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask,
- 0)
- .IsApplicable(context.get(), transformation_context));
- ASSERT_TRUE(
- transformation3.IsApplicable(context.get(), transformation_context));
- transformation3.Apply(context.get(), &transformation_context);
+ {
+ // Creates the first operand.
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 2),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- TransformationSetMemoryOperandsMask transformation4(
- MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
- SpvMemoryAccessVolatileMask, 1);
- ASSERT_TRUE(
- transformation4.IsApplicable(context.get(), transformation_context));
- transformation4.Apply(context.get(), &transformation_context);
+ {
+ // Creates both operands.
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(21, SpvOpCopyMemory, 3),
+ SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- TransformationSetMemoryOperandsMask transformation5(
- MakeInstructionDescriptor(147, SpvOpLoad, 0),
- SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
- ASSERT_TRUE(
- transformation5.IsApplicable(context.get(), transformation_context));
- transformation5.Apply(context.get(), &transformation_context);
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+ SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
+ // Bad: the first mask is None, so Aligned cannot be added to it.
+ ASSERT_FALSE(
+ TransformationSetMemoryOperandsMask(
+ MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
+ SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0)
+ .IsApplicable(context.get(), transformation_context));
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
- TransformationSetMemoryOperandsMask transformation6(
- MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
- 0);
- ASSERT_TRUE(
- transformation6.IsApplicable(context.get(), transformation_context));
- transformation6.Apply(context.get(), &transformation_context);
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
+ SpvMemoryAccessVolatileMask, 1);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(147, SpvOpLoad, 0),
+ SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
+
+ {
+ TransformationSetMemoryOperandsMask transformation(
+ MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
+ 0);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ }
std::string after_transformation = R"(
OpCapability Shader
@@ -430,6 +486,8 @@
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
+ OpCopyMemory %133 %12 Nontemporal|Volatile
+ OpCopyMemory %133 %12 None Nontemporal|Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None Aligned|Nontemporal 16
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
new file mode 100644
index 0000000..4383e07
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
@@ -0,0 +1,145 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapConditionalBranchOperandsTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21 10 20
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Invalid instruction descriptor.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(26, SpvOpPhi, 0), 26)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Descriptor for a wrong instruction.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(25, SpvOpPhi, 0), 26)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Fresh id is not fresh.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 25)
+ .IsApplicable(context.get(), transformation_context));
+
+ TransformationSwapConditionalBranchOperands transformation(
+ MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ %26 = OpLogicalNot %14 %15
+ OpSelectionMerge %17 None
+ OpBranchConditional %26 %21 %16 20 10
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
index a29c511..f72fc95 100644
--- a/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_vector_shuffle_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_vector_shuffle.h"
+
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -540,6 +541,171 @@
.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationVectorShuffle, HandlesIrrelevantIds1) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypeVector %6 2
+ %10 = OpConstantTrue %6
+ %11 = OpConstantFalse %6
+ %12 = OpConstantComposite %7 %10 %11
+ %112 = OpConstantComposite %7 %11 %10
+ %13 = OpTypeVector %6 3
+ %16 = OpConstantComposite %13 %10 %11 %10
+ %17 = OpTypeVector %6 4
+ %20 = OpConstantComposite %17 %10 %11 %10 %11
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeVector %21 2
+ %25 = OpConstant %21 1
+ %26 = OpConstant %21 0
+ %27 = OpConstantComposite %22 %25 %26
+ %28 = OpTypeVector %21 3
+ %31 = OpConstantComposite %28 %25 %26 %25
+ %32 = OpTypeVector %21 4
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %25 %26 %25 %26
+ %36 = OpTypeInt 32 0
+ %37 = OpTypeVector %36 2
+ %40 = OpConstant %36 1
+ %41 = OpConstant %36 0
+ %42 = OpConstantComposite %37 %40 %41
+ %43 = OpTypeVector %36 3
+ %46 = OpConstantComposite %43 %40 %41 %40
+ %47 = OpTypeVector %36 4
+ %50 = OpConstantComposite %47 %40 %41 %40 %41
+ %51 = OpTypeFloat 32
+ %55 = OpConstant %51 1
+ %56 = OpConstant %51 0
+ %58 = OpTypeVector %51 3
+ %61 = OpConstantComposite %58 %55 %56 %55
+ %62 = OpTypeVector %51 4
+ %65 = OpConstantComposite %62 %55 %56 %55 %56
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %100 None
+ OpBranchConditional %10 %101 %102
+ %101 = OpLabel
+ %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+ OpBranch %100
+ %102 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ TransformationVectorShuffle transformation(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
+ MakeDataDescriptor(200, {1})));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(112, {0}),
+ MakeDataDescriptor(200, {0})));
+}
+
+TEST(TransformationVectorShuffle, HandlesIrrelevantIds2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypeVector %6 2
+ %10 = OpConstantTrue %6
+ %11 = OpConstantFalse %6
+ %12 = OpConstantComposite %7 %10 %11
+ %112 = OpConstantComposite %7 %11 %10
+ %13 = OpTypeVector %6 3
+ %16 = OpConstantComposite %13 %10 %11 %10
+ %17 = OpTypeVector %6 4
+ %20 = OpConstantComposite %17 %10 %11 %10 %11
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeVector %21 2
+ %25 = OpConstant %21 1
+ %26 = OpConstant %21 0
+ %27 = OpConstantComposite %22 %25 %26
+ %28 = OpTypeVector %21 3
+ %31 = OpConstantComposite %28 %25 %26 %25
+ %32 = OpTypeVector %21 4
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %25 %26 %25 %26
+ %36 = OpTypeInt 32 0
+ %37 = OpTypeVector %36 2
+ %40 = OpConstant %36 1
+ %41 = OpConstant %36 0
+ %42 = OpConstantComposite %37 %40 %41
+ %43 = OpTypeVector %36 3
+ %46 = OpConstantComposite %43 %40 %41 %40
+ %47 = OpTypeVector %36 4
+ %50 = OpConstantComposite %47 %40 %41 %40 %41
+ %51 = OpTypeFloat 32
+ %55 = OpConstant %51 1
+ %56 = OpConstant %51 0
+ %58 = OpTypeVector %51 3
+ %61 = OpConstantComposite %58 %55 %56 %55
+ %62 = OpTypeVector %51 4
+ %65 = OpConstantComposite %62 %55 %56 %55 %56
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %100 None
+ OpBranchConditional %10 %101 %102
+ %101 = OpLabel
+ %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+ OpBranch %100
+ %102 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ fact_manager.AddFactIdIsIrrelevant(112);
+ TransformationVectorShuffle transformation(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(12, {0}),
+ MakeDataDescriptor(200, {1})));
+ ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(112, {0}),
+ MakeDataDescriptor(200, {0})));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/name_mapper_test.cpp b/third_party/SPIRV-Tools/test/name_mapper_test.cpp
index 00fbeed..759c705 100644
--- a/third_party/SPIRV-Tools/test/name_mapper_test.cpp
+++ b/third_party/SPIRV-Tools/test/name_mapper_test.cpp
@@ -270,6 +270,7 @@
BuiltInCase("SubgroupLocalInvocationId"),
BuiltInGLCase("VertexIndex"),
BuiltInGLCase("InstanceIndex"),
+ BuiltInGLCase("BaseInstance"),
BuiltInCase("SubgroupEqMaskKHR"),
BuiltInCase("SubgroupGeMaskKHR"),
BuiltInCase("SubgroupGtMaskKHR"),
diff --git a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
index e7303ba..125543d 100644
--- a/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/aggressive_dead_code_elim_test.cpp
@@ -413,7 +413,7 @@
predefs1 + names_after + predefs2 + func_after, true, true);
}
-TEST_F(AggressiveDCETest, OptWhitelistExtension) {
+TEST_F(AggressiveDCETest, OptAllowListExtension) {
// #version 140
//
// in vec4 BaseColor;
@@ -498,7 +498,7 @@
predefs1 + names_after + predefs2 + func_after, true, true);
}
-TEST_F(AggressiveDCETest, NoOptBlacklistExtension) {
+TEST_F(AggressiveDCETest, NoOptDenyListExtension) {
// #version 140
//
// in vec4 BaseColor;
@@ -6761,6 +6761,770 @@
predefs1 + names_after + predefs2_after + func_after, true, true);
}
+TEST_F(AggressiveDCETest, MultipleFunctionProcessIndependently) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %entryHistogram "entryHistogram" %gl_GlobalInvocationID %gl_LocalInvocationIndex
+ OpEntryPoint GLCompute %entryAverage "entryAverage" %gl_GlobalInvocationID %gl_LocalInvocationIndex
+ OpExecutionMode %entryHistogram LocalSize 16 16 1
+ OpExecutionMode %entryAverage LocalSize 256 1 1
+ OpSource HLSL 640
+ OpName %type_RWStructuredBuffer_uint "type.RWStructuredBuffer.uint"
+ OpName %uHistogram "uHistogram"
+ OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+ OpMemberName %type_ACSBuffer_counter 0 "counter"
+ OpName %counter_var_uHistogram "counter.var.uHistogram"
+ OpName %sharedHistogram "sharedHistogram"
+ OpName %entryHistogram "entryHistogram"
+ OpName %param_var_id "param.var.id"
+ OpName %param_var_idx "param.var.idx"
+ OpName %entryAverage "entryAverage"
+ OpName %param_var_id_0 "param.var.id"
+ OpName %param_var_idx_0 "param.var.idx"
+ OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+ OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+ OpDecorate %uHistogram DescriptorSet 0
+ OpDecorate %uHistogram Binding 0
+ OpDecorate %counter_var_uHistogram DescriptorSet 0
+ OpDecorate %counter_var_uHistogram Binding 1
+ OpDecorate %_runtimearr_uint ArrayStride 4
+ OpMemberDecorate %type_RWStructuredBuffer_uint 0 Offset 0
+ OpDecorate %type_RWStructuredBuffer_uint BufferBlock
+ OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+ OpDecorate %type_ACSBuffer_counter BufferBlock
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %uint_4 = OpConstant %uint 4
+ %uint_8 = OpConstant %uint 8
+ %uint_16 = OpConstant %uint 16
+ %uint_32 = OpConstant %uint 32
+ %uint_64 = OpConstant %uint 64
+ %uint_128 = OpConstant %uint 128
+ %uint_256 = OpConstant %uint 256
+ %uint_512 = OpConstant %uint 512
+ %uint_254 = OpConstant %uint 254
+ %uint_255 = OpConstant %uint 255
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%type_RWStructuredBuffer_uint = OpTypeStruct %_runtimearr_uint
+%_ptr_Uniform_type_RWStructuredBuffer_uint = OpTypePointer Uniform %type_RWStructuredBuffer_uint
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+%_arr_uint_uint_256 = OpTypeArray %uint %uint_256
+%_ptr_Workgroup__arr_uint_uint_256 = OpTypePointer Workgroup %_arr_uint_uint_256
+ %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%_ptr_Input_uint = OpTypePointer Input %uint
+ %void = OpTypeVoid
+ %49 = OpTypeFunction %void
+%_ptr_Function_v3uint = OpTypePointer Function %v3uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+ %52 = OpTypeFunction %void %_ptr_Function_v3uint %_ptr_Function_uint
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+ %uint_264 = OpConstant %uint 264
+ %bool = OpTypeBool
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+ %uHistogram = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_uint Uniform
+%counter_var_uHistogram = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+%sharedHistogram = OpVariable %_ptr_Workgroup__arr_uint_uint_256 Workgroup
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%entryHistogram = OpFunction %void None %49
+ %57 = OpLabel
+%param_var_id = OpVariable %_ptr_Function_v3uint Function
+%param_var_idx = OpVariable %_ptr_Function_uint Function
+ %58 = OpLoad %v3uint %gl_GlobalInvocationID
+ %59 = OpLoad %uint %gl_LocalInvocationIndex
+ %79 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %int_0
+ %80 = OpAtomicIAdd %uint %79 %uint_1 %uint_0 %uint_1
+ OpReturn
+ OpFunctionEnd
+%entryAverage = OpFunction %void None %49
+ %63 = OpLabel
+%param_var_id_0 = OpVariable %_ptr_Function_v3uint Function
+%param_var_idx_0 = OpVariable %_ptr_Function_uint Function
+ %64 = OpLoad %v3uint %gl_GlobalInvocationID
+ %65 = OpLoad %uint %gl_LocalInvocationIndex
+ OpStore %param_var_idx_0 %65
+ %83 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+ OpStore %83 %uint_0
+
+; CHECK: [[ieq:%\w+]] = OpIEqual
+; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]]
+; CHECK-NEXT: OpBranchConditional [[ieq]] [[not_elim:%\w+]] [[merge]]
+; CHECK-NEXT: [[not_elim]] = OpLabel
+; CHECK: [[merge]] = OpLabel
+
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %85 = OpIEqual %bool %65 %uint_0
+ OpSelectionMerge %89 None
+ OpBranchConditional %85 %86 %89
+ %86 = OpLabel
+ %88 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+ OpStore %88 %uint_1
+ OpBranch %89
+ %89 = OpLabel
+ OpControlBarrier %uint_2 %uint_2 %uint_264
+ %91 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65
+ %92 = OpLoad %uint %91
+ %94 = OpAccessChain %_ptr_Uniform_uint %uHistogram %int_0 %65
+ OpStore %94 %92
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
+
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) {
+ // Verify that dead local variable tc and store eliminated but all
+ // in-function debuginfo kept.
+ //
+ // The SPIR-V has been inlined and local single store eliminated
+ //
+ // Texture2D g_tColor;
+ // SamplerState g_sAniso;
+ //
+ // struct PS_INPUT {
+ // float2 vTextureCoords : TEXCOORD2;
+ // };
+ //
+ // struct PS_OUTPUT {
+ // float4 vColor : SV_Target0;
+ // };
+ //
+ // PS_OUTPUT MainPs(PS_INPUT i) {
+ // PS_OUTPUT ps_output;
+ // float2 tc = i.vTextureCoords.xy;
+ // ps_output.vColor = g_tColor.Sample(g_sAniso, tc);
+ // return ps_output;
+ // }
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %7 = OpString "foo.frag"
+ %8 = OpString "PS_OUTPUT"
+ %9 = OpString "float"
+ %10 = OpString "vColor"
+ %11 = OpString "PS_INPUT"
+ %12 = OpString "vTextureCoords"
+ %13 = OpString "@type.2d.image"
+ %14 = OpString "type.2d.image"
+ %15 = OpString "Texture2D.TemplateParam"
+ %16 = OpString "src.MainPs"
+ %17 = OpString "tc"
+ %18 = OpString "ps_output"
+ %19 = OpString "i"
+ %20 = OpString "@type.sampler"
+ %21 = OpString "type.sampler"
+ %22 = OpString "g_sAniso"
+ %23 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %45 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %51 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %53 = OpExtInst %void %1 DebugOperation Deref
+ %54 = OpExtInst %void %1 DebugExpression %53
+ %55 = OpExtInst %void %1 DebugSource %7
+ %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+ %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+ %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+ %60 = OpExtInst %void %1 DebugTypeVector %59 4
+ %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+ %63 = OpExtInst %void %1 DebugTypeVector %59 2
+ %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+ %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+ %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+ %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+ %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+ %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+ %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+ %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+ %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+ %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+ %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+ %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %45
+ %76 = OpLabel
+ %107 = OpExtInst %void %1 DebugScope %69
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
+ %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %79 = OpVariable %_ptr_Function_v2float Function
+ %108 = OpExtInst %void %1 DebugNoScope
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
+ %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %82 = OpLoad %v2float %in_var_TEXCOORD2
+ %83 = OpCompositeConstruct %PS_INPUT %82
+ OpStore %param_var_i %83
+ %109 = OpExtInst %void %1 DebugScope %68
+ %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+ %110 = OpExtInst %void %1 DebugScope %69
+ %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52
+ OpLine %7 19 17
+ %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %89 = OpLoad %v2float %88
+ OpLine %7 19 12
+ OpStore %79 %89
+;CHECK-NOT: OpStore %79 %89
+ OpLine %7 19 12
+ %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52
+ OpLine %7 20 26
+ %91 = OpLoad %type_2d_image %g_tColor
+ OpLine %7 20 46
+ %92 = OpLoad %type_sampler %g_sAniso
+ OpLine %7 20 26
+ %94 = OpSampledImage %type_sampled_image %91 %92
+ %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+ OpLine %7 20 5
+ %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+ OpStore %96 %95
+ OpLine %7 21 12
+ %97 = OpLoad %PS_OUTPUT %78
+ OpLine %7 21 5
+ OpStore %81 %97
+ %111 = OpExtInst %void %1 DebugNoScope
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
+ %100 = OpCompositeExtract %v4float %97 0
+ OpStore %out_var_SV_Target0 %100
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) {
+ // Verify that local variable tc and its store are kept by DebugDeclare.
+ //
+ // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V
+ // has just been inlined.
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %20 = OpString "foo.frag"
+ %24 = OpString "PS_OUTPUT"
+ %28 = OpString "float"
+ %31 = OpString "vColor"
+ %33 = OpString "PS_INPUT"
+ %38 = OpString "vTextureCoords"
+ %40 = OpString "@type.2d.image"
+ %41 = OpString "type.2d.image"
+ %43 = OpString "Texture2D.TemplateParam"
+ %47 = OpString "src.MainPs"
+ %51 = OpString "tc"
+ %53 = OpString "ps_output"
+ %56 = OpString "i"
+ %58 = OpString "@type.sampler"
+ %59 = OpString "type.sampler"
+ %61 = OpString "g_sAniso"
+ %63 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %65 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %39 = OpExtInst %void %1 DebugInfoNone
+ %55 = OpExtInst %void %1 DebugExpression
+ %22 = OpExtInst %void %1 DebugSource %20
+ %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL
+ %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27
+ %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float
+ %30 = OpExtInst %void %1 DebugTypeVector %29 4
+ %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36
+ %37 = OpExtInst %void %1 DebugTypeVector %29 2
+ %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate
+ %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0
+ %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44
+ %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35
+ %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39
+ %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48
+ %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal
+ %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal
+ %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1
+ %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate
+ %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition
+ %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %65
+ %66 = OpLabel
+ %114 = OpExtInst %void %1 DebugScope %50
+ %98 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %99 = OpVariable %_ptr_Function_v2float Function
+ %115 = OpExtInst %void %1 DebugNoScope
+ %100 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %70 = OpLoad %v2float %in_var_TEXCOORD2
+ %71 = OpCompositeConstruct %PS_INPUT %70
+ OpStore %param_var_i %71
+ %116 = OpExtInst %void %1 DebugScope %48
+ %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55
+ %117 = OpExtInst %void %1 DebugScope %50
+ %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55
+ OpLine %20 19 17
+ %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %105 = OpLoad %v2float %104
+ OpLine %20 19 12
+ OpStore %99 %105
+;CHECK: OpStore %99 %105
+ %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+ OpLine %20 20 26
+ %107 = OpLoad %type_2d_image %g_tColor
+ OpLine %20 20 46
+ %108 = OpLoad %type_sampler %g_sAniso
+ OpLine %20 20 26
+ %110 = OpSampledImage %type_sampled_image %107 %108
+ %111 = OpImageSampleImplicitLod %v4float %110 %105 None
+ OpLine %20 20 5
+ %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0
+ OpStore %112 %111
+ OpLine %20 21 12
+ %113 = OpLoad %PS_OUTPUT %98
+ OpLine %20 21 5
+ OpStore %100 %113
+ %118 = OpExtInst %void %1 DebugNoScope
+ %73 = OpLoad %PS_OUTPUT %100
+ %74 = OpCompositeExtract %v4float %73 0
+ OpStore %out_var_SV_Target0 %74
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoValueDerefKeepsStore) {
+ // Verify that local variable tc and its store are kept by DebugValue with
+ // Deref.
+ //
+ // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V
+ // has just been inlined and edited to replace the DebugDeclare with the
+ // DebugValue/Deref.
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %7 = OpString "foo.frag"
+ %8 = OpString "PS_OUTPUT"
+ %9 = OpString "float"
+ %10 = OpString "vColor"
+ %11 = OpString "PS_INPUT"
+ %12 = OpString "vTextureCoords"
+ %13 = OpString "@type.2d.image"
+ %14 = OpString "type.2d.image"
+ %15 = OpString "Texture2D.TemplateParam"
+ %16 = OpString "src.MainPs"
+ %17 = OpString "tc"
+ %18 = OpString "ps_output"
+ %19 = OpString "i"
+ %20 = OpString "@type.sampler"
+ %21 = OpString "type.sampler"
+ %22 = OpString "g_sAniso"
+ %23 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %45 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %51 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %53 = OpExtInst %void %1 DebugOperation Deref
+ %54 = OpExtInst %void %1 DebugExpression %53
+ %55 = OpExtInst %void %1 DebugSource %7
+ %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+ %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+ %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+ %60 = OpExtInst %void %1 DebugTypeVector %59 4
+ %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+ %63 = OpExtInst %void %1 DebugTypeVector %59 2
+ %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+ %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+ %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+ %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+ %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+ %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+ %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+ %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+ %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+ %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+ %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+ %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %45
+ %76 = OpLabel
+ %101 = OpExtInst %void %1 DebugScope %69
+ %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %79 = OpVariable %_ptr_Function_v2float Function
+ %102 = OpExtInst %void %1 DebugNoScope
+ %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %82 = OpLoad %v2float %in_var_TEXCOORD2
+ %83 = OpCompositeConstruct %PS_INPUT %82
+ OpStore %param_var_i %83
+ %103 = OpExtInst %void %1 DebugScope %68
+ %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+ %104 = OpExtInst %void %1 DebugScope %69
+ %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+ OpLine %7 19 17
+ %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %89 = OpLoad %v2float %88
+ OpLine %7 19 12
+ OpStore %79 %89
+;CHECK: OpStore %79 %89
+ %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+ OpLine %7 20 26
+ %91 = OpLoad %type_2d_image %g_tColor
+ OpLine %7 20 46
+ %92 = OpLoad %type_sampler %g_sAniso
+ OpLine %7 20 26
+ %94 = OpSampledImage %type_sampled_image %91 %92
+ %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+ OpLine %7 20 5
+ %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+ OpStore %96 %95
+ OpLine %7 21 12
+ %97 = OpLoad %PS_OUTPUT %78
+ OpLine %7 21 5
+ OpStore %81 %97
+ %105 = OpExtInst %void %1 DebugNoScope
+ %99 = OpLoad %PS_OUTPUT %81
+ %100 = OpCompositeExtract %v4float %99 0
+ OpStore %out_var_SV_Target0 %100
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
+TEST_F(AggressiveDCETest, DebugInfoElimUnusedTextureKeepGlobalVariable) {
+ // Verify that unused texture g_tColor2 is eliminated but its
+ // DebugGlobalVariable is retained but with DebugInfoNone for its Variable.
+ //
+ // Same shader source as DebugInfoInFunctionKeepStoreVarElim but with unused
+ // g_tColor2 added.
+
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_tColor2 %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %21 = OpString "foo6.frag"
+ %25 = OpString "PS_OUTPUT"
+ %29 = OpString "float"
+ %32 = OpString "vColor"
+ %34 = OpString "PS_INPUT"
+ %39 = OpString "vTextureCoords"
+ %41 = OpString "@type.2d.image"
+ %42 = OpString "type.2d.image"
+ %44 = OpString "Texture2D.TemplateParam"
+ %48 = OpString "src.MainPs"
+ %52 = OpString "tc"
+ %54 = OpString "ps_output"
+ %57 = OpString "i"
+ %59 = OpString "@type.sampler"
+ %60 = OpString "type.sampler"
+ %62 = OpString "g_sAniso"
+ %64 = OpString "g_tColor2"
+ %66 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %g_tColor2 "g_tColor2"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_tColor2 DescriptorSet 0
+ OpDecorate %g_tColor2 Binding 1
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 2
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %68 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %78 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+;CHECK-NOT: %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %40 = OpExtInst %void %1 DebugInfoNone
+ %56 = OpExtInst %void %1 DebugExpression
+ %23 = OpExtInst %void %1 DebugSource %21
+ %24 = OpExtInst %void %1 DebugCompilationUnit 1 4 %23 HLSL
+ %27 = OpExtInst %void %1 DebugTypeComposite %25 Structure %23 11 1 %24 %25 %uint_128 FlagIsProtected|FlagIsPrivate %28
+ %30 = OpExtInst %void %1 DebugTypeBasic %29 %uint_32 Float
+ %31 = OpExtInst %void %1 DebugTypeVector %30 4
+ %28 = OpExtInst %void %1 DebugTypeMember %32 %31 %23 13 5 %27 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %36 = OpExtInst %void %1 DebugTypeComposite %34 Structure %23 6 1 %24 %34 %uint_64 FlagIsProtected|FlagIsPrivate %37
+ %38 = OpExtInst %void %1 DebugTypeVector %30 2
+ %37 = OpExtInst %void %1 DebugTypeMember %39 %38 %23 8 5 %36 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %43 = OpExtInst %void %1 DebugTypeComposite %41 Class %23 0 0 %24 %42 %40 FlagIsProtected|FlagIsPrivate
+ %45 = OpExtInst %void %1 DebugTypeTemplateParameter %44 %30 %40 %23 0 0
+ %46 = OpExtInst %void %1 DebugTypeTemplate %43 %45
+ %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %27 %36
+ %49 = OpExtInst %void %1 DebugFunction %48 %47 %23 16 1 %24 %48 FlagIsProtected|FlagIsPrivate 17 %40
+ %51 = OpExtInst %void %1 DebugLexicalBlock %23 17 1 %49
+ %53 = OpExtInst %void %1 DebugLocalVariable %52 %38 %23 20 12 %51 FlagIsLocal
+ %55 = OpExtInst %void %1 DebugLocalVariable %54 %27 %23 18 15 %51 FlagIsLocal
+ %58 = OpExtInst %void %1 DebugLocalVariable %57 %36 %23 16 29 %49 FlagIsLocal 1
+ %61 = OpExtInst %void %1 DebugTypeComposite %59 Structure %23 0 0 %24 %60 %40 FlagIsProtected|FlagIsPrivate
+ %63 = OpExtInst %void %1 DebugGlobalVariable %62 %61 %23 4 14 %24 %62 %g_sAniso FlagIsDefinition
+ %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition
+;CHECK-NOT: %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition
+;CHECK: %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %40 FlagIsDefinition
+ %67 = OpExtInst %void %1 DebugGlobalVariable %66 %43 %23 1 11 %24 %66 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %68
+ %69 = OpLabel
+ %117 = OpExtInst %void %1 DebugScope %51
+ %101 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %102 = OpVariable %_ptr_Function_v2float Function
+ %118 = OpExtInst %void %1 DebugNoScope
+ %103 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %73 = OpLoad %v2float %in_var_TEXCOORD2
+ %74 = OpCompositeConstruct %PS_INPUT %73
+ OpStore %param_var_i %74
+ %119 = OpExtInst %void %1 DebugScope %49
+ %105 = OpExtInst %void %1 DebugDeclare %58 %param_var_i %56
+ %120 = OpExtInst %void %1 DebugScope %51
+ %106 = OpExtInst %void %1 DebugDeclare %55 %101 %56
+ OpLine %21 20 17
+ %107 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %108 = OpLoad %v2float %107
+ OpLine %21 20 12
+ OpStore %102 %108
+ %109 = OpExtInst %void %1 DebugDeclare %53 %102 %56
+ OpLine %21 21 26
+ %110 = OpLoad %type_2d_image %g_tColor
+ OpLine %21 21 46
+ %111 = OpLoad %type_sampler %g_sAniso
+ OpLine %21 21 57
+ %112 = OpLoad %v2float %102
+ OpLine %21 21 26
+ %113 = OpSampledImage %type_sampled_image %110 %111
+ %114 = OpImageSampleImplicitLod %v4float %113 %112 None
+ OpLine %21 21 5
+ %115 = OpAccessChain %_ptr_Function_v4float %101 %int_0
+ OpStore %115 %114
+ OpLine %21 22 12
+ %116 = OpLoad %PS_OUTPUT %101
+ OpLine %21 22 5
+ OpStore %103 %116
+ %121 = OpExtInst %void %1 DebugNoScope
+ %76 = OpLoad %PS_OUTPUT %103
+ %77 = OpCompositeExtract %v4float %76 0
+ OpStore %out_var_SV_Target0 %77
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_2);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<AggressiveDCEPass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Check that logical addressing required
diff --git a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
index f1460c5..7381908 100644
--- a/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/block_merge_test.cpp
@@ -639,6 +639,40 @@
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
+TEST_F(BlockMergeTest, DontMergeTerminateInvocation) {
+ const std::string text = R"(
+; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[ret:%\w+]]
+; CHECK: [[ret:%\w+]] = OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-DAG: [[cont]] = OpLabel
+; CHECK-DAG: [[merge]] = OpLabel
+OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+%void = OpTypeVoid
+%bool = OpTypeBool
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%1 = OpLabel
+OpBranch %2
+%2 = OpLabel
+OpLoopMerge %3 %4 None
+OpBranch %5
+%5 = OpLabel
+OpTerminateInvocation
+%4 = OpLabel
+OpBranch %2
+%3 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<BlockMergePass>(text, true);
+}
+
TEST_F(BlockMergeTest, DontMergeUnreachable) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
diff --git a/third_party/SPIRV-Tools/test/opt/ccp_test.cpp b/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
index 920c0f4..52a291c 100644
--- a/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ccp_test.cpp
@@ -925,6 +925,137 @@
SinglePassRunAndMatch<CCPPass>(text, true);
}
+TEST_F(CCPTest, DebugSimpleFoldConstant) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability Linkage
+ %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ %file_name = OpString "test"
+ %float_name = OpString "float"
+ %main_name = OpString "main"
+ %f_name = OpString "f"
+ OpDecorate %1 LinkageAttributes "func" Export
+ %void = OpTypeVoid
+ %bool = OpTypeBool
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+
+; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
+ %float_1 = OpConstant %float 1
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %8 = OpTypeFunction %float
+ %null_expr = OpExtInst %void %ext DebugExpression
+ %src = OpExtInst %void %ext DebugSource %file_name
+ %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+ %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+ %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+ %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1
+ %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+ %1 = OpFunction %float None %8
+ %10 = OpLabel
+
+; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
+; CHECK: OpLine [[file:%\w+]] 1 0
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1
+ %s0 = OpExtInst %void %ext DebugScope %dbg_main
+ OpLine %file_name 1 0
+ %17 = OpFAdd %float %float_0 %float_1
+ %val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr
+
+; CHECK: OpLine [[file]] 2 0
+; CHECK: OpReturnValue [[float1]]
+ OpLine %file_name 2 0
+ OpReturnValue %17
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<CCPPass>(text, true);
+}
+
+TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %outparm
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ %file_name = OpString "test"
+ %float_name = OpString "float"
+ %main_name = OpString "main"
+ %f_name = OpString "f"
+ OpName %main "main"
+ OpName %outparm "outparm"
+ OpDecorate %outparm Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %bool = OpTypeBool
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_4 = OpConstant %int 4
+ %int_3 = OpConstant %int 3
+ %int_1 = OpConstant %int 1
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+%_ptr_Output_int = OpTypePointer Output %int
+ %outparm = OpVariable %_ptr_Output_int Output
+ %null_expr = OpExtInst %void %ext DebugExpression
+ %src = OpExtInst %void %ext DebugSource %file_name
+ %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+ %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+ %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+ %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+ %bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
+ %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
+ %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
+ %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
+ %dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+ %dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal
+ %dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal
+ %main = OpFunction %void None %3
+ %4 = OpLabel
+
+; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope
+; CHECK: OpLine [[file:%\w+]] 1 0
+; CHECK: OpIAdd %int %int_4 %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7
+ %s0 = OpExtInst %void %ext DebugScope %bb0
+ OpLine %file_name 1 0
+ %9 = OpIAdd %int %int_4 %int_3
+ %val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr
+
+; CHECK: OpLine [[file]] 2 0
+; CHECK: OpSGreaterThan %bool %int_7 %int_3
+; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true
+ OpLine %file_name 2 0
+ %6 = OpSGreaterThan %bool %9 %int_3
+ %val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr
+
+ OpSelectionMerge %25 None
+ OpBranchConditional %6 %22 %23
+ %22 = OpLabel
+ %s1 = OpExtInst %void %ext DebugScope %bb1
+ %7 = OpCopyObject %int %9
+ %val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr
+ OpBranch %25
+ %23 = OpLabel
+ %s2 = OpExtInst %void %ext DebugScope %bb2
+ %8 = OpCopyObject %int %int_4
+ OpBranch %25
+ %25 = OpLabel
+ %s3 = OpExtInst %void %ext DebugScope %bb3
+ %35 = OpPhi %int %7 %22 %8 %23
+ OpStore %outparm %35
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<CCPPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp b/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
index 1afee9c..72bc7f6 100644
--- a/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/copy_prop_array_test.cpp
@@ -1622,6 +1622,198 @@
SinglePassRunAndMatch<CopyPropagateArrays>(text, true);
}
+TEST_F(CopyPropArrayPassTest, DebugDeclare) {
+ const std::string before =
+ R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %23 %null_expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+TEST_F(CopyPropArrayPassTest, DebugValue) {
+ const std::string before =
+ R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+%deref = OpExtInst %void %ext DebugOperation Deref
+%expr = OpExtInst %void %ext DebugExpression %deref
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%decl = OpExtInst %void %ext DebugValue %dbg_f %23 %expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
index 3dcc0f7..3abb53d 100644
--- a/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
@@ -186,6 +186,81 @@
true, true);
}
+TEST_F(DeadBranchElimTest, IfThenElseNull) {
+ // For booleans OpConstantNull should be treated similar to OpConstantFalse.
+ //
+ // From the SPIR-V spec:
+ // OpConstantNull: Declares a new null constant value.
+ // The null value is type dependent, defined as follows:
+ // - Scalar Boolean: false
+ // ...
+
+ const std::string predefs =
+ R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %gl_FragColor "gl_FragColor"
+OpName %BaseColor "BaseColor"
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%bool = OpTypeBool
+%9 = OpConstantNull %bool
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%float_0 = OpConstant %float 0
+%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+)";
+
+ const std::string before =
+ R"(%main = OpFunction %void None %7
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+OpSelectionMerge %20 None
+OpBranchConditional %9 %21 %22
+%21 = OpLabel
+OpStore %v %14
+OpBranch %20
+%22 = OpLabel
+OpStore %v %16
+OpBranch %20
+%20 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%main = OpFunction %void None %7
+%19 = OpLabel
+%v = OpVariable %_ptr_Function_v4float Function
+OpBranch %22
+%22 = OpLabel
+OpStore %v %16
+OpBranch %20
+%20 = OpLabel
+%23 = OpLoad %v4float %v
+OpStore %gl_FragColor %23
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
+ true, true);
+}
+
TEST_F(DeadBranchElimTest, IfThenTrue) {
// #version 140
//
@@ -3229,6 +3304,101 @@
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
}
+TEST_F(DeadBranchElimTest, DebugInformation) {
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+%name = OpString "test"
+OpName %main "main"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%5 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%float_0 = OpConstant %float 0
+
+; CHECK: [[value:%\w+]] = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %name %ty %src 0 0 %cu %name FlagIsProtected|FlagIsPrivate 0 %main
+
+; CHECK: [[bb1:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLexicalBlock [[src:%\w+]] 1 0 [[dbg_main:%\w+]]
+; CHECK: [[bb2:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 2 0 [[dbg_main]]
+; CHECK: [[bb3:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 3 0 [[dbg_main]]
+%bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main
+%bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main
+%bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main
+
+%dbg_f = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float
+; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty:%\w+]] [[src]] 0 0 [[dbg_main]]
+%dbg_foo = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 0 0 %dbg_main FlagIsLocal
+; CHECK: [[dbg_bar:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty]] [[src]] 1 0 [[bb3]]
+%dbg_bar = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 1 0 %bb3 FlagIsLocal
+
+%main = OpFunction %void None %5
+%17 = OpLabel
+; CHECK-NOT: DebugScope [[dbg_main]]
+; CHECK-NOT: OpLine {{%\w+}} 0 0
+%scope0 = OpExtInst %void %ext DebugScope %dbg_main
+OpLine %name 0 0
+OpSelectionMerge %18 None
+OpBranchConditional %true %19 %20
+%19 = OpLabel
+; CHECK: DebugScope [[bb1]]
+; CHECK: OpLine {{%\w+}} 1 0
+%scope1 = OpExtInst %void %ext DebugScope %bb1
+OpLine %name 1 0
+OpBranch %18
+%20 = OpLabel
+; CHECK-NOT: DebugScope [[bb2]]
+; CHECK-NOT: OpLine {{%\w+}} 2 0
+%scope2 = OpExtInst %void %ext DebugScope %bb2
+OpLine %name 2 0
+OpBranch %18
+%18 = OpLabel
+
+; CHECK: DebugScope [[bb3]]
+; CHECK-NOT: OpLine {{%\w+}} 3 0
+; CHECK: DebugValue [[dbg_foo]] [[value]]
+; CHECK: OpLine {{%\w+}} 4 0
+; CHECK: OpStore %gl_FragColor [[value]]
+; CHECK: DebugDeclare [[dbg_bar]] %gl_FragColor
+; CHECK: DebugValue [[dbg_bar]] [[value]]
+%scope3 = OpExtInst %void %ext DebugScope %bb3
+OpLine %name 3 0
+%21 = OpPhi %v4float %12 %19 %14 %20
+%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %21 %null_expr
+OpLine %name 4 0
+OpStore %gl_FragColor %21
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_bar %gl_FragColor %null_expr
+%decl2 = OpExtInst %void %ext DebugValue %dbg_bar %21 %null_expr
+OpLine %name 5 0
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
diff --git a/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp b/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
index f19737f..82890f7 100644
--- a/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/debug_info_manager_test.cpp
@@ -431,6 +431,74 @@
EXPECT_EQ(inst, before_100);
}
+TEST(DebugInfoManager, KillDebugDeclares) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %in_var_COLOR
+ OpExecutionMode %main OriginUpperLeft
+ %5 = OpString "ps.hlsl"
+ %14 = OpString "#line 1 \"ps.hlsl\"
+void main(float in_var_color : COLOR) {
+ float color = in_var_color;
+}
+"
+ %17 = OpString "float"
+ %21 = OpString "main"
+ %24 = OpString "color"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %main "main"
+ OpDecorate %in_var_COLOR Location 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+ %void = OpTypeVoid
+ %27 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+ %13 = OpExtInst %void %1 DebugExpression
+ %15 = OpExtInst %void %1 DebugSource %5 %14
+ %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL
+ %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float
+ %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
+ %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
+ %12 = OpExtInst %void %1 DebugInfoNone
+ %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
+ %main = OpFunction %void None %27
+ %28 = OpLabel
+ %100 = OpVariable %_ptr_Function_float Function
+ %31 = OpLoad %float %in_var_COLOR
+ OpStore %100 %31
+ %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+ %37 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+ %38 = OpExtInst %void %1 DebugDeclare %25 %100 %13
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ auto* dbg_info_mgr = context->get_debug_info_mgr();
+ auto* def_use_mgr = context->get_def_use_mgr();
+
+ EXPECT_TRUE(dbg_info_mgr->IsDebugDeclared(100));
+ EXPECT_EQ(def_use_mgr->GetDef(36)->GetOpenCL100DebugOpcode(),
+ OpenCLDebugInfo100DebugDeclare);
+ EXPECT_EQ(def_use_mgr->GetDef(37)->GetOpenCL100DebugOpcode(),
+ OpenCLDebugInfo100DebugDeclare);
+ EXPECT_EQ(def_use_mgr->GetDef(38)->GetOpenCL100DebugOpcode(),
+ OpenCLDebugInfo100DebugDeclare);
+
+ dbg_info_mgr->KillDebugDeclares(100);
+ EXPECT_EQ(def_use_mgr->GetDef(36), nullptr);
+ EXPECT_EQ(def_use_mgr->GetDef(37), nullptr);
+ EXPECT_EQ(def_use_mgr->GetDef(38), nullptr);
+ EXPECT_FALSE(dbg_info_mgr->IsDebugDeclared(100));
+}
+
} // namespace
} // namespace analysis
} // namespace opt
diff --git a/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp b/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
index 11074c3..cdcc9a8 100644
--- a/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/desc_sroa_test.cpp
@@ -25,7 +25,116 @@
using DescriptorScalarReplacementTest = PassTest<::testing::Test>;
-TEST_F(DescriptorScalarReplacementTest, ExpandTexture) {
+std::string GetStructureArrayTestSpirv() {
+ // The SPIR-V for the following high-level shader:
+ // Flattening structures and arrays should result in the following binding
+ // numbers. Only the ones that are actually used in the shader should be in
+ // the final SPIR-V.
+ //
+ // globalS[0][0].t[0] 0 (used)
+ // globalS[0][0].t[1] 1
+ // globalS[0][0].s[0] 2 (used)
+ // globalS[0][0].s[1] 3
+ // globalS[0][1].t[0] 4
+ // globalS[0][1].t[1] 5
+ // globalS[0][1].s[0] 6
+ // globalS[0][1].s[1] 7
+ // globalS[1][0].t[0] 8
+ // globalS[1][0].t[1] 9
+ // globalS[1][0].s[0] 10
+ // globalS[1][0].s[1] 11
+ // globalS[1][1].t[0] 12
+ // globalS[1][1].t[1] 13 (used)
+ // globalS[1][1].s[0] 14
+ // globalS[1][1].s[1] 15 (used)
+
+ /*
+ struct S {
+ Texture2D t[2];
+ SamplerState s[2];
+ };
+
+ S globalS[2][2];
+
+ float4 main() : SV_Target {
+ return globalS[0][0].t[0].Sample(globalS[0][0].s[0], float2(0,0)) +
+ globalS[1][1].t[1].Sample(globalS[1][1].s[1], float2(0,0));
+ }
+ */
+
+ return R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out_var_SV_Target
+ OpExecutionMode %main OriginUpperLeft
+ OpName %S "S"
+ OpMemberName %S 0 "t"
+ OpMemberName %S 1 "s"
+ OpName %type_2d_image "type.2d.image"
+ OpName %type_sampler "type.sampler"
+ OpName %globalS "globalS"
+ OpName %out_var_SV_Target "out.var.SV_Target"
+ OpName %main "main"
+ OpName %src_main "src.main"
+ OpName %bb_entry "bb.entry"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %out_var_SV_Target Location 0
+ OpDecorate %globalS DescriptorSet 0
+ OpDecorate %globalS Binding 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+ %v2float = OpTypeVector %float 2
+ %10 = OpConstantComposite %v2float %float_0 %float_0
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
+ %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_type_sampler_uint_2
+%_arr_S_uint_2 = OpTypeArray %S %uint_2
+%_arr__arr_S_uint_2_uint_2 = OpTypeArray %_arr_S_uint_2 %uint_2
+%_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 = OpTypePointer UniformConstant %_arr__arr_S_uint_2_uint_2
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %24 = OpTypeFunction %void
+ %28 = OpTypeFunction %v4float
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %globalS = OpVariable %_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 UniformConstant
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+ %main = OpFunction %void None %24
+ %25 = OpLabel
+ %26 = OpFunctionCall %v4float %src_main
+ OpStore %out_var_SV_Target %26
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %v4float None %28
+ %bb_entry = OpLabel
+ %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_0 %int_0 %int_0 %int_0
+ %32 = OpLoad %type_2d_image %31
+ %34 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_0 %int_0 %int_1 %int_0
+ %35 = OpLoad %type_sampler %34
+ %37 = OpSampledImage %type_sampled_image %32 %35
+ %38 = OpImageSampleImplicitLod %v4float %37 %10 None
+ %39 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_1 %int_1 %int_0 %int_1
+ %40 = OpLoad %type_2d_image %39
+ %41 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_1 %int_1 %int_1 %int_1
+ %42 = OpLoad %type_sampler %41
+ %43 = OpSampledImage %type_sampled_image %40 %42
+ %44 = OpImageSampleImplicitLod %v4float %43 %10 None
+ %45 = OpFAdd %v4float %38 %44
+ OpReturnValue %45
+ OpFunctionEnd
+ )";
+}
+
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfTextures) {
const std::string text = R"(
; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
; CHECK: OpDecorate [[var1]] Binding 0
@@ -94,7 +203,7 @@
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
}
-TEST_F(DescriptorScalarReplacementTest, ExpandSampler) {
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) {
const std::string text = R"(
; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0
; CHECK: OpDecorate [[var1]] Binding 1
@@ -145,7 +254,7 @@
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
}
-TEST_F(DescriptorScalarReplacementTest, ExpandSSBO) {
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) {
// Tests the expansion of an SSBO. Also check that an access chain with more
// than 1 index is correctly handled.
const std::string text = R"(
@@ -265,6 +374,361 @@
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
}
+
+TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) {
+ // Checks that constant buffers are not expanded.
+ // Constant buffers are represented as global structures, but they should not
+ // be replaced with new variables for their elements.
+ /*
+ cbuffer MyCbuffer : register(b1) {
+ float2 a;
+ float2 b;
+ };
+ float main() : A {
+ return a.x + b.y;
+ }
+ */
+ const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
+; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %out_var_A
+ OpSource HLSL 600
+ OpName %type_MyCbuffer "type.MyCbuffer"
+ OpMemberName %type_MyCbuffer 0 "a"
+ OpMemberName %type_MyCbuffer 1 "b"
+ OpName %MyCbuffer "MyCbuffer"
+ OpName %out_var_A "out.var.A"
+ OpName %main "main"
+ OpDecorate %out_var_A Location 0
+ OpDecorate %MyCbuffer DescriptorSet 0
+ OpDecorate %MyCbuffer Binding 1
+ OpMemberDecorate %type_MyCbuffer 0 Offset 0
+ OpMemberDecorate %type_MyCbuffer 1 Offset 8
+ OpDecorate %type_MyCbuffer Block
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %float = OpTypeFloat 32
+ %v2float = OpTypeVector %float 2
+%type_MyCbuffer = OpTypeStruct %v2float %v2float
+%_ptr_Uniform_type_MyCbuffer = OpTypePointer Uniform %type_MyCbuffer
+%_ptr_Output_float = OpTypePointer Output %float
+ %void = OpTypeVoid
+ %13 = OpTypeFunction %void
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+ %MyCbuffer = OpVariable %_ptr_Uniform_type_MyCbuffer Uniform
+ %out_var_A = OpVariable %_ptr_Output_float Output
+ %main = OpFunction %void None %13
+ %15 = OpLabel
+ %16 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0
+ %17 = OpLoad %float %16
+ %18 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1
+ %19 = OpLoad %float %18
+ %20 = OpFAdd %float %17 %19
+ OpStore %out_var_A %20
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) {
+ // Checks that structured buffers are not expanded.
+ // Structured buffers are represented as global structures, that have one
+ // member which is a runtime array.
+ /*
+ struct S {
+ float2 a;
+ float2 b;
+ };
+ RWStructuredBuffer<S> sb;
+ float main() : A {
+ return sb[0].a.x + sb[0].b.x;
+ }
+ */
+ const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
+; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %out_var_A
+ OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+ OpName %S "S"
+ OpMemberName %S 0 "a"
+ OpMemberName %S 1 "b"
+ OpName %sb "sb"
+ OpName %out_var_A "out.var.A"
+ OpName %main "main"
+ OpDecorate %out_var_A Location 0
+ OpDecorate %sb DescriptorSet 0
+ OpDecorate %sb Binding 0
+ OpMemberDecorate %S 0 Offset 0
+ OpMemberDecorate %S 1 Offset 8
+ OpDecorate %_runtimearr_S ArrayStride 16
+ OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+ OpDecorate %type_RWStructuredBuffer_S BufferBlock
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %int_1 = OpConstant %int 1
+ %float = OpTypeFloat 32
+ %v2float = OpTypeVector %float 2
+ %S = OpTypeStruct %v2float %v2float
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+%_ptr_Output_float = OpTypePointer Output %float
+ %void = OpTypeVoid
+ %17 = OpTypeFunction %void
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+ %sb = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+ %out_var_A = OpVariable %_ptr_Output_float Output
+ %main = OpFunction %void None %17
+ %19 = OpLabel
+ %20 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0
+ %21 = OpLoad %float %20
+ %22 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0
+ %23 = OpLoad %float %22
+ %24 = OpFAdd %float %21 %23
+ OpStore %out_var_A %24
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) {
+ // Checks that names are properly generated for multi-dimension arrays and
+ // structure members.
+ const std::string checks = R"(
+; CHECK: OpName %globalS_0__0__t_0_ "globalS[0][0].t[0]"
+; CHECK: OpName %globalS_0__0__s_0_ "globalS[0][0].s[0]"
+; CHECK: OpName %globalS_1__1__t_1_ "globalS[1][1].t[1]"
+; CHECK: OpName %globalS_1__1__s_1_ "globalS[1][1].s[1]"
+ )";
+
+ const std::string text = checks + GetStructureArrayTestSpirv();
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) {
+ // Checks that flattening structures and arrays results in correct binding
+ // numbers.
+ const std::string checks = R"(
+; CHECK: OpDecorate %globalS_0__0__t_0_ Binding 0
+; CHECK: OpDecorate %globalS_0__0__s_0_ Binding 2
+; CHECK: OpDecorate %globalS_1__1__t_1_ Binding 13
+; CHECK: OpDecorate %globalS_1__1__s_1_ Binding 15
+ )";
+
+ const std::string text = checks + GetStructureArrayTestSpirv();
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) {
+ // Checks that all access chains indexing into structures and/or arrays are
+ // replaced with direct access to replacement variables.
+ const std::string checks = R"(
+; CHECK-NOT: OpAccessChain
+; CHECK: OpLoad %type_2d_image %globalS_0__0__t_0_
+; CHECK: OpLoad %type_sampler %globalS_0__0__s_0_
+; CHECK: OpLoad %type_2d_image %globalS_1__1__t_1_
+; CHECK: OpLoad %type_sampler %globalS_1__1__s_1_
+ )";
+
+ const std::string text = checks + GetStructureArrayTestSpirv();
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
+}
+
+TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) {
+ // Checks that a mix of OpAccessChain, OpLoad, and OpCompositeExtract patterns
+ // can be properly replaced with replacement variables.
+ // This pattern can be seen when a global structure of resources is passed to
+ // a function.
+
+ /* High-level source:
+ // globalS[0].t[0] binding: 0 (used)
+ // globalS[0].t[1] binding: 1 (used)
+ // globalS[0].tt[0].s[0] binding: 2
+ // globalS[0].tt[0].s[1] binding: 3 (used)
+ // globalS[0].tt[0].s[2] binding: 4
+ // globalS[0].tt[1].s[0] binding: 5
+ // globalS[0].tt[1].s[1] binding: 6
+ // globalS[0].tt[1].s[2] binding: 7 (used)
+ // globalS[1].t[0] binding: 8 (used)
+ // globalS[1].t[1] binding: 9 (used)
+ // globalS[1].tt[0].s[0] binding: 10
+ // globalS[1].tt[0].s[1] binding: 11 (used)
+ // globalS[1].tt[0].s[2] binding: 12
+ // globalS[1].tt[1].s[0] binding: 13
+ // globalS[1].tt[1].s[1] binding: 14
+ // globalS[1].tt[1].s[2] binding: 15 (used)
+
+ struct T {
+ SamplerState s[3];
+ };
+
+ struct S {
+ Texture2D t[2];
+ T tt[2];
+ };
+
+ float4 tex2D(S x, float2 v) {
+ return x.t[0].Sample(x.tt[0].s[1], v) + x.t[1].Sample(x.tt[1].s[2], v);
+ }
+
+ S globalS[2];
+
+ float4 main() : SV_Target {
+ return tex2D(globalS[0], float2(0,0)) + tex2D(globalS[1], float2(0,0)) ;
+ }
+ */
+ const std::string shader = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %out_var_SV_Target
+ OpExecutionMode %main OriginUpperLeft
+ OpName %S "S"
+ OpMemberName %S 0 "t"
+ OpMemberName %S 1 "tt"
+ OpName %type_2d_image "type.2d.image"
+ OpName %T "T"
+ OpMemberName %T 0 "s"
+ OpName %type_sampler "type.sampler"
+ OpName %globalS "globalS"
+ OpName %out_var_SV_Target "out.var.SV_Target"
+ OpName %main "main"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %out_var_SV_Target Location 0
+ OpDecorate %globalS DescriptorSet 0
+ OpDecorate %globalS Binding 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %float = OpTypeFloat 32
+ %float_0 = OpConstant %float 0
+ %v2float = OpTypeVector %float 2
+ %14 = OpConstantComposite %v2float %float_0 %float_0
+ %int_1 = OpConstant %int 1
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2
+ %uint_3 = OpConstant %uint 3
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3
+ %T = OpTypeStruct %_arr_type_sampler_uint_3
+%_arr_T_uint_2 = OpTypeArray %T %uint_2
+ %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_T_uint_2
+%_arr_S_uint_2 = OpTypeArray %S %uint_2
+%_ptr_UniformConstant__arr_S_uint_2 = OpTypePointer UniformConstant %_arr_S_uint_2
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %27 = OpTypeFunction %void
+%_ptr_UniformConstant_S = OpTypePointer UniformConstant %S
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %globalS = OpVariable %_ptr_UniformConstant__arr_S_uint_2 UniformConstant
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+ %main = OpFunction %void None %27
+ %29 = OpLabel
+ %30 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_0
+ %31 = OpLoad %S %30
+ %32 = OpCompositeExtract %_arr_type_2d_image_uint_2 %31 0
+ %33 = OpCompositeExtract %type_2d_image %32 0
+ %34 = OpCompositeExtract %type_2d_image %32 1
+ %35 = OpCompositeExtract %_arr_T_uint_2 %31 1
+ %36 = OpCompositeExtract %T %35 0
+ %37 = OpCompositeExtract %_arr_type_sampler_uint_3 %36 0
+ %38 = OpCompositeExtract %type_sampler %37 1
+ %39 = OpCompositeExtract %T %35 1
+ %40 = OpCompositeExtract %_arr_type_sampler_uint_3 %39 0
+ %41 = OpCompositeExtract %type_sampler %40 2
+ %42 = OpSampledImage %type_sampled_image %33 %38
+ %43 = OpImageSampleImplicitLod %v4float %42 %14 None
+ %44 = OpSampledImage %type_sampled_image %34 %41
+ %45 = OpImageSampleImplicitLod %v4float %44 %14 None
+ %46 = OpFAdd %v4float %43 %45
+ %47 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_1
+ %48 = OpLoad %S %47
+ %49 = OpCompositeExtract %_arr_type_2d_image_uint_2 %48 0
+ %50 = OpCompositeExtract %type_2d_image %49 0
+ %51 = OpCompositeExtract %type_2d_image %49 1
+ %52 = OpCompositeExtract %_arr_T_uint_2 %48 1
+ %53 = OpCompositeExtract %T %52 0
+ %54 = OpCompositeExtract %_arr_type_sampler_uint_3 %53 0
+ %55 = OpCompositeExtract %type_sampler %54 1
+ %56 = OpCompositeExtract %T %52 1
+ %57 = OpCompositeExtract %_arr_type_sampler_uint_3 %56 0
+ %58 = OpCompositeExtract %type_sampler %57 2
+ %59 = OpSampledImage %type_sampled_image %50 %55
+ %60 = OpImageSampleImplicitLod %v4float %59 %14 None
+ %61 = OpSampledImage %type_sampled_image %51 %58
+ %62 = OpImageSampleImplicitLod %v4float %61 %14 None
+ %63 = OpFAdd %v4float %60 %62
+ %64 = OpFAdd %v4float %46 %63
+ OpStore %out_var_SV_Target %64
+ OpReturn
+ OpFunctionEnd
+)";
+
+ const std::string checks = R"(
+; CHECK: OpName %globalS_0__t_0_ "globalS[0].t[0]"
+; CHECK: OpName %globalS_0__t_1_ "globalS[0].t[1]"
+; CHECK: OpName %globalS_1__t_0_ "globalS[1].t[0]"
+; CHECK: OpName %globalS_1__t_1_ "globalS[1].t[1]"
+; CHECK: OpName %globalS_0__tt_0__s_1_ "globalS[0].tt[0].s[1]"
+; CHECK: OpName %globalS_0__tt_1__s_2_ "globalS[0].tt[1].s[2]"
+; CHECK: OpName %globalS_1__tt_0__s_1_ "globalS[1].tt[0].s[1]"
+; CHECK: OpName %globalS_1__tt_1__s_2_ "globalS[1].tt[1].s[2]"
+; CHECK: OpDecorate %globalS_0__t_0_ Binding 0
+; CHECK: OpDecorate %globalS_0__t_1_ Binding 1
+; CHECK: OpDecorate %globalS_1__t_0_ Binding 8
+; CHECK: OpDecorate %globalS_1__t_1_ Binding 9
+; CHECK: OpDecorate %globalS_0__tt_0__s_1_ Binding 3
+; CHECK: OpDecorate %globalS_0__tt_1__s_2_ Binding 7
+; CHECK: OpDecorate %globalS_1__tt_0__s_1_ Binding 11
+; CHECK: OpDecorate %globalS_1__tt_1__s_2_ Binding 15
+
+; CHECK: %globalS_0__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_0__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_1__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_1__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+; CHECK: %globalS_0__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_0__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_1__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+; CHECK: %globalS_1__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+
+; CHECK: [[img_1:%\w+]] = OpLoad %type_2d_image %globalS_0__t_0_
+; CHECK: [[img_2:%\w+]] = OpLoad %type_2d_image %globalS_0__t_1_
+; CHECK: [[sampler_1:%\w+]] = OpLoad %type_sampler %globalS_0__tt_0__s_1_
+; CHECK: [[sampler_2:%\w+]] = OpLoad %type_sampler %globalS_0__tt_1__s_2_
+
+; CHECK: [[sampled_img_1:%\w+]] = OpSampledImage %type_sampled_image [[img_1]] [[sampler_1]]
+; CHECK: [[sample_1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_1]]
+; CHECK: [[sampled_img_2:%\w+]] = OpSampledImage %type_sampled_image [[img_2]] [[sampler_2]]
+; CHECK: [[sample_2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_2]]
+; CHECK: OpFAdd %v4float [[sample_1]] [[sample_2]]
+
+; CHECK: [[img_3:%\w+]] = OpLoad %type_2d_image %globalS_1__t_0_
+; CHECK: [[img_4:%\w+]] = OpLoad %type_2d_image %globalS_1__t_1_
+; CHECK: [[sampler_3:%\w+]] = OpLoad %type_sampler %globalS_1__tt_0__s_1_
+; CHECK: [[sampler_4:%\w+]] = OpLoad %type_sampler %globalS_1__tt_1__s_2_
+
+; CHECK: [[sampled_img_3:%\w+]] = OpSampledImage %type_sampled_image [[img_3]] [[sampler_3]]
+; CHECK: [[sample_3:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_3]]
+; CHECK: [[sampled_img_4:%\w+]] = OpSampledImage %type_sampled_image [[img_4]] [[sampler_4]]
+; CHECK: [[sample_4:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_4]]
+; CHECK: OpFAdd %v4float [[sample_3]] [[sample_4]]
+)";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(checks + shader, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp b/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
index 43b723e..534f770 100644
--- a/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
+++ b/third_party/SPIRV-Tools/test/opt/dominator_tree/generated.cpp
@@ -895,6 +895,126 @@
}
}
+TEST_F(PassClassTest, DominationForInstructions) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeBool
+ %8 = OpConstantTrue %7
+ %9 = OpConstant %6 37
+ %10 = OpConstant %6 3
+ %13 = OpConstant %6 5
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpIAdd %6 %9 %10
+ %15 = OpISub %6 %12 %13
+ OpSelectionMerge %18 None
+ OpBranchConditional %8 %16 %17
+ %16 = OpLabel
+ %20 = OpISub %6 %12 %13
+ OpBranch %18
+ %17 = OpLabel
+ %21 = OpISub %6 %12 %13
+ OpBranch %18
+ %18 = OpLabel
+ %22 = OpISub %6 %12 %13
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ // clang-format on
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ EXPECT_NE(nullptr, context->module()) << "Assembling failed for shader:\n"
+ << text << std::endl;
+
+ {
+ const DominatorAnalysis* dominator_analysis = context->GetDominatorAnalysis(
+ spvtest::GetFunction(context->module(), 4));
+
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_FALSE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(12)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(5),
+ context->get_def_use_mgr()->GetDef(12)));
+ EXPECT_FALSE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12),
+ context->get_def_use_mgr()->GetDef(5)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(16)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(21)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(18)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(22)));
+ EXPECT_FALSE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(20),
+ context->get_def_use_mgr()->GetDef(22)));
+ EXPECT_FALSE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(21),
+ context->get_def_use_mgr()->GetDef(22)));
+ EXPECT_TRUE(
+ dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(15)));
+ }
+ {
+ const PostDominatorAnalysis* post_dominator_analysis =
+ context->GetPostDominatorAnalysis(
+ spvtest::GetFunction(context->module(), 4));
+
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(12)));
+ EXPECT_FALSE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(12),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(12),
+ context->get_def_use_mgr()->GetDef(5)));
+ EXPECT_FALSE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(5),
+ context->get_def_use_mgr()->GetDef(12)));
+ EXPECT_FALSE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(16),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_FALSE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(21),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(18),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(22),
+ context->get_def_use_mgr()->GetDef(15)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(22),
+ context->get_def_use_mgr()->GetDef(20)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(22),
+ context->get_def_use_mgr()->GetDef(21)));
+ EXPECT_TRUE(post_dominator_analysis->Dominates(
+ context->get_def_use_mgr()->GetDef(15),
+ context->get_def_use_mgr()->GetDef(15)));
+ }
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/inline_test.cpp b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
index fc2197c..ffd3e38 100644
--- a/third_party/SPIRV-Tools/test/opt/inline_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/inline_test.cpp
@@ -2453,6 +2453,103 @@
SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
}
+TEST_F(InlineTest, DontInlineFuncWithOpTerminateInvocationInContinue) {
+ const std::string test =
+ R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpBranch %9
+%9 = OpLabel
+OpLoopMerge %11 %12 None
+OpBranch %13
+%13 = OpLabel
+OpBranchConditional %true %10 %11
+%10 = OpLabel
+OpBranch %12
+%12 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpBranch %9
+%11 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
+}
+
+TEST_F(InlineTest, InlineFuncWithOpTerminateInvocationNotInContinue) {
+ const std::string before =
+ R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+%16 = OpFunctionCall %void %kill_
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 330
+OpName %main "main"
+OpName %kill_ "kill("
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpTerminateInvocation
+%18 = OpLabel
+OpReturn
+OpFunctionEnd
+%kill_ = OpFunction %void None %3
+%7 = OpLabel
+OpTerminateInvocation
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
+}
+
TEST_F(InlineTest, EarlyReturnFunctionInlined) {
// #version 140
//
diff --git a/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
index e72561c..437fe73 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_context_test.cpp
@@ -26,6 +26,9 @@
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandValueIndex = 5;
+
namespace spvtools {
namespace opt {
namespace {
@@ -867,6 +870,235 @@
<< bb->id(); // Make sure asan does not complain about use after free.
}
+TEST_F(IRContextTest, DebugInstructionReplaceSingleUse) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+ std::unique_ptr<IRContext> ctx =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+ DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+ pass.Run(ctx.get());
+ EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+ auto* dbg_value = ctx->get_def_use_mgr()->GetDef(24);
+ EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 22);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
+ dbg_value = ctx->get_def_use_mgr()->GetDef(24);
+ EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 7);
+
+ auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+ EXPECT_TRUE(
+ dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+ dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+ EXPECT_TRUE(
+ dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+}
+
+TEST_F(IRContextTest, DebugInstructionReplaceAllUses) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%27 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 1 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+%28 = OpExtInst %3 %1 DebugValue %27 %22 %10
+%29 = OpExtInst %3 %1 DebugDeclare %27 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+ std::unique_ptr<IRContext> ctx =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+ DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+ pass.Run(ctx.get());
+ EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+ auto* dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
+ auto* dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
+ EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 22);
+ EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 22);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
+ dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
+ dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
+ EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 7);
+ EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
+ 7);
+
+ auto* dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
+ auto* dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
+ EXPECT_TRUE(
+ dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+ EXPECT_TRUE(
+ dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+ dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
+ dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
+ EXPECT_TRUE(
+ dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+ EXPECT_TRUE(
+ dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
+}
+
+TEST_F(IRContextTest, AddDebugValueAfterReplaceUse) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+%2 = OpString "test"
+%3 = OpTypeVoid
+%4 = OpTypeFunction %3
+%5 = OpTypeFloat 32
+%6 = OpTypePointer Function %5
+%7 = OpConstant %5 0
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 32
+%10 = OpExtInst %3 %1 DebugExpression
+%11 = OpExtInst %3 %1 DebugSource %2
+%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
+%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
+%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
+%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
+%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
+%17 = OpFunction %3 None %4
+%18 = OpLabel
+%19 = OpExtInst %3 %1 DebugScope %14
+%20 = OpVariable %6 Function
+%26 = OpVariable %6 Function
+OpBranch %21
+%21 = OpLabel
+%27 = OpExtInst %3 %1 DebugScope %14
+%22 = OpPhi %5 %7 %18
+OpBranch %23
+%23 = OpLabel
+%28 = OpExtInst %3 %1 DebugScope %14
+OpLine %2 0 0
+OpStore %20 %7
+%24 = OpExtInst %3 %1 DebugValue %16 %22 %10
+%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
+OpReturn
+OpFunctionEnd)";
+
+ std::unique_ptr<IRContext> ctx =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
+ DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
+ pass.Run(ctx.get());
+ EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
+
+ // Replace all uses of result it '26' with '20'
+ auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+ EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+ 26);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
+ dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
+ EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+ 20);
+
+ // No DebugValue should be added because result id '26' is not used for
+ // DebugDeclare.
+ ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 26, 22, dbg_decl);
+ EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn);
+
+ // DebugValue should be added because result id '20' is used for DebugDeclare.
+ ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 20, 22, dbg_decl);
+ EXPECT_EQ(dbg_decl->NextNode()->GetOpenCL100DebugOpcode(),
+ OpenCLDebugInfo100DebugValue);
+
+ // Replace all uses of result it '20' with '26'
+ EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+ 20);
+ EXPECT_TRUE(ctx->ReplaceAllUsesWith(20, 26));
+ EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
+ 26);
+
+ // No DebugValue should be added because result id '20' is not used for
+ // DebugDeclare.
+ ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 20, 7, dbg_decl);
+ Instruction* dbg_value = dbg_decl->NextNode();
+ EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
+ EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 22);
+
+ // DebugValue should be added because result id '26' is used for DebugDeclare.
+ ctx->get_debug_info_mgr()->AddDebugValue(dbg_decl, 26, 7, dbg_decl);
+ dbg_value = dbg_decl->NextNode();
+ EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
+ EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp b/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
index 402352d..8e1cee6 100644
--- a/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_single_block_elim.cpp
@@ -1118,6 +1118,340 @@
)";
SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
}
+
+TEST_F(LocalSingleBlockLoadStoreElimTest, DebugDeclareTest) {
+ // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+ // optimized, but stores are not deleted for store/store.
+ //
+ // struct PS_INPUT {
+ // float4 c0 : COLOR0;
+ // float4 c1 : COLOR1;
+ // };
+ //
+ // struct PS_OUTPUT {
+ // float4 vColor : SV_Target0;
+ // };
+ //
+ // PS_OUTPUT MainPs(PS_INPUT i) {
+ // PS_OUTPUT ps_output;
+ // float4 c;
+ // c = i.c0;
+ // c += i.c1;
+ // c /= 2.0;
+ // ps_output.vColor = c;
+ // return ps_output;
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %6 = OpString "foo3.frag"
+ %7 = OpString "PS_OUTPUT"
+ %8 = OpString "float"
+ %9 = OpString "vColor"
+ %10 = OpString "PS_INPUT"
+ %11 = OpString "c1"
+ %12 = OpString "c0"
+ %13 = OpString "src.MainPs"
+ %14 = OpString "c"
+ %15 = OpString "ps_output"
+ %16 = OpString "i"
+ OpName %in_var_COLOR0 "in.var.COLOR0"
+ OpName %in_var_COLOR1 "in.var.COLOR1"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "c0"
+ OpMemberName %PS_INPUT 1 "c1"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %src_MainPs "src.MainPs"
+ OpName %i "i"
+ OpName %bb_entry "bb.entry"
+ OpName %ps_output "ps_output"
+ OpName %c "c"
+ OpDecorate %in_var_COLOR0 Location 0
+ OpDecorate %in_var_COLOR1 Location 1
+ OpDecorate %out_var_SV_Target0 Location 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %float = OpTypeFloat 32
+ %float_2 = OpConstant %float 2
+ %v4float = OpTypeVector %float 4
+ %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_256 = OpConstant %uint 256
+ %40 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %45 = OpExtInst %void %1 DebugSource %6
+ %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL
+ %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48
+ %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+ %50 = OpExtInst %void %1 DebugTypeVector %49 4
+ %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53
+ %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+ %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51
+ %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs
+ %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55
+ %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal
+ %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal
+ %59 = OpExtInst %void %1 DebugExpression
+ %60 = OpExtInst %void %1 DebugLocalVariable %16 %51 %45 13 29 %55 FlagIsLocal 1
+ %61 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %55 FlagIsLocal 1
+ %MainPs = OpFunction %void None %40
+ %62 = OpLabel
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %63 = OpLoad %v4float %in_var_COLOR0
+ %64 = OpLoad %v4float %in_var_COLOR1
+ %65 = OpCompositeConstruct %PS_INPUT %63 %64
+ OpStore %param_var_i %65
+ %66 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i
+ %67 = OpCompositeExtract %v4float %66 0
+ OpStore %out_var_SV_Target0 %67
+ OpReturn
+ OpFunctionEnd
+ OpLine %6 13 1
+ %src_MainPs = OpFunction %PS_OUTPUT None %42
+ %83 = OpExtInst %void %1 DebugScope %55
+ OpLine %6 13 29
+ %i = OpFunctionParameter %_ptr_Function_PS_INPUT
+ %69 = OpExtInst %void %1 DebugDeclare %60 %i %59
+ %84 = OpExtInst %void %1 DebugNoScope
+ %bb_entry = OpLabel
+ %85 = OpExtInst %void %1 DebugScope %56
+ %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %c = OpVariable %_ptr_Function_v4float Function
+ %71 = OpExtInst %void %1 DebugDeclare %61 %c %59
+ OpLine %6 18 9
+ %72 = OpAccessChain %_ptr_Function_v4float %i %int_0
+ OpLine %6 18 13
+ %73 = OpLoad %v4float %72
+ OpLine %6 18 5
+ OpStore %c %73
+;CHECK: OpStore %c %73
+ OpLine %6 19 10
+ %74 = OpAccessChain %_ptr_Function_v4float %i %int_1
+ OpLine %6 19 14
+ %75 = OpLoad %v4float %74
+ OpLine %6 19 5
+ %76 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 19 5
+;CHECK-NOT: %76 = OpLoad %v4float %c
+ OpLine %6 19 7
+ %77 = OpFAdd %v4float %76 %75
+;CHECK-NOT: %77 = OpFAdd %v4float %76 %75
+;CHECK: %77 = OpFAdd %v4float %73 %75
+ OpLine %6 19 5
+ OpStore %c %77
+;CHECK: OpStore %c %77
+ OpLine %6 20 5
+ %78 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 20 5
+;CHECK-NOT: %78 = OpLoad %v4float %c
+ OpLine %6 20 7
+ %79 = OpFDiv %v4float %78 %31
+;CHECK-NOT %79 = OpFDiv %v4float %78 %31
+;CHECK: %79 = OpFDiv %v4float %77 %31
+ OpLine %6 20 5
+ OpStore %c %79
+;CHECK: OpStore %c %79
+ OpLine %6 22 26
+ %80 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 22 26
+;CHECK-NOT: %80 = OpLoad %v4float %c
+ OpLine %6 22 5
+ %81 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
+ OpStore %81 %80
+;CHECK-NOT: OpStore %81 %80
+;CHECK: OpStore %81 %79
+ OpLine %6 23 12
+ %82 = OpLoad %PS_OUTPUT %ps_output
+ OpLine %6 23 5
+ OpReturnValue %82
+ %86 = OpExtInst %void %1 DebugNoScope
+ OpFunctionEnd
+ )";
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+TEST_F(LocalSingleBlockLoadStoreElimTest, DebugValueTest) {
+ // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+ // optimized, but stores are not deleted for store/store.
+ // Same source as DebugDeclareTest; DebugDeclare replaced with
+ // equivalent DebugValue
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %6 = OpString "foo3.frag"
+ %7 = OpString "PS_OUTPUT"
+ %8 = OpString "float"
+ %9 = OpString "vColor"
+ %10 = OpString "PS_INPUT"
+ %11 = OpString "c1"
+ %12 = OpString "c0"
+ %13 = OpString "src.MainPs"
+ %14 = OpString "c"
+ %15 = OpString "ps_output"
+ %16 = OpString "i"
+ OpName %in_var_COLOR0 "in.var.COLOR0"
+ OpName %in_var_COLOR1 "in.var.COLOR1"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "c0"
+ OpMemberName %PS_INPUT 1 "c1"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %src_MainPs "src.MainPs"
+ OpName %i "i"
+ OpName %bb_entry "bb.entry"
+ OpName %ps_output "ps_output"
+ OpName %c "c"
+ OpDecorate %in_var_COLOR0 Location 0
+ OpDecorate %in_var_COLOR1 Location 1
+ OpDecorate %out_var_SV_Target0 Location 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %float = OpTypeFloat 32
+ %float_2 = OpConstant %float 2
+ %v4float = OpTypeVector %float 4
+ %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_256 = OpConstant %uint 256
+ %40 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %45 = OpExtInst %void %1 DebugSource %6
+ %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL
+ %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48
+ %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+ %50 = OpExtInst %void %1 DebugTypeVector %49 4
+ %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53
+ %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+ %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51
+ %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs
+ %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55
+ %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal
+ %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal
+ %59 = OpExtInst %void %1 DebugExpression %60 = OpExtInst %void %1 DebugOperation Deref %61 = OpExtInst
+%void %1 DebugExpression %60 %62 = OpExtInst %void %1 DebugLocalVariable %16 %51
+%45 13 29 %55 FlagIsLocal 1 %63 = OpExtInst %void %1 DebugLocalVariable %14 %50
+%45 16 12 %55 FlagIsLocal 1 %MainPs = OpFunction %void None %40 %64 = OpLabel
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %65 = OpLoad %v4float %in_var_COLOR0
+ %66 = OpLoad %v4float %in_var_COLOR1
+ %67 = OpCompositeConstruct %PS_INPUT %65 %66
+ OpStore %param_var_i %67
+ %68 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i
+ %69 = OpCompositeExtract %v4float %68 0
+ OpStore %out_var_SV_Target0 %69
+ OpReturn
+ OpFunctionEnd
+ OpLine %6 13 1
+ %src_MainPs = OpFunction %PS_OUTPUT None %42
+ %70 = OpExtInst %void %1 DebugScope %55
+ OpLine %6 13 29
+ %i = OpFunctionParameter %_ptr_Function_PS_INPUT
+ %71 = OpExtInst %void %1 DebugDeclare %62 %i %59
+ %72 = OpExtInst %void %1 DebugNoScope
+ %bb_entry = OpLabel
+ %73 = OpExtInst %void %1 DebugScope %56
+ %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %c = OpVariable %_ptr_Function_v4float Function
+ %74 = OpExtInst %void %1 DebugValue %63 %c %61
+ OpLine %6 18 9
+ %75 = OpAccessChain %_ptr_Function_v4float %i %int_0
+ OpLine %6 18 13
+ %76 = OpLoad %v4float %75
+ OpLine %6 18 5
+ OpStore %c %76
+;CHECK: OpStore %c %76
+ OpLine %6 19 10
+ %77 = OpAccessChain %_ptr_Function_v4float %i %int_1
+ OpLine %6 19 14
+ %78 = OpLoad %v4float %77
+ OpLine %6 19 5
+ %79 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 19 5
+;CHECK-NOT: %79 = OpLoad %v4float %c
+ OpLine %6 19 7
+ %80 = OpFAdd %v4float %79 %78
+;CHECK-NOT: %80 = OpFAdd %v4float %79 %78
+;CHECK: %80 = OpFAdd %v4float %76 %78
+ OpLine %6 19 5
+ OpStore %c %80
+;CHECK: OpStore %c %80
+ OpLine %6 20 5
+ %81 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 20 5
+;CHECK-NOT: %81 = OpLoad %v4float %c
+ OpLine %6 20 7
+ %82 = OpFDiv %v4float %81 %31
+;CHECK-NOT: %82 = OpFDiv %v4float %81 %31
+;CHECK: %82 = OpFDiv %v4float %80 %31
+ OpLine %6 20 5
+ OpStore %c %82
+;CHECK: OpStore %c %82
+ OpLine %6 22 26
+ %83 = OpLoad %v4float %c
+;CHECK-NOT: OpLine %6 22 26
+;CHECK-NOT: %83 = OpLoad %v4float %c
+ OpLine %6 22 5
+ %84 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0
+ OpStore %84 %83
+;CHECK-NOT: OpStore %84 %83
+;CHECK: OpStore %84 %82
+ OpLine %6 23 12
+ %85 = OpLoad %PS_OUTPUT %ps_output
+ OpLine %6 23 5
+ OpReturnValue %85
+ %86 = OpExtInst %void %1 DebugNoScope
+ OpFunctionEnd
+ )";
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other target variable types
diff --git a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
index 5a1650b..ebc89a6 100644
--- a/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_single_store_elim_test.cpp
@@ -902,6 +902,315 @@
)";
SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
}
+
+TEST_F(LocalSingleStoreElimTest, DebugDeclareTest) {
+ // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+ // optimized, DebugValue placed after the store and the associated
+ // DebugDeclare is removed.
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %20 = OpString "foo.frag"
+ %24 = OpString "PS_OUTPUT"
+ %28 = OpString "float"
+ %31 = OpString "vColor"
+ %33 = OpString "PS_INPUT"
+ %38 = OpString "vTextureCoords"
+ %40 = OpString "@type.2d.image"
+ %41 = OpString "type.2d.image"
+ %43 = OpString "Texture2D.TemplateParam"
+ %47 = OpString "src.MainPs"
+ %51 = OpString "tc"
+ %53 = OpString "ps_output"
+ %56 = OpString "i"
+ %58 = OpString "@type.sampler"
+ %59 = OpString "type.sampler"
+ %61 = OpString "g_sAniso"
+ %63 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %65 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %39 = OpExtInst %void %1 DebugInfoNone
+ %55 = OpExtInst %void %1 DebugExpression
+ %22 = OpExtInst %void %1 DebugSource %20
+ %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL
+ %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27
+ %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float
+ %30 = OpExtInst %void %1 DebugTypeVector %29 4
+ %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36
+ %37 = OpExtInst %void %1 DebugTypeVector %29 2
+ %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate
+ %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0
+ %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44
+ %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35
+ %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39
+ %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48
+ %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal
+ %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal
+ %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1
+ %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate
+ %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition
+ %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %65
+ %66 = OpLabel
+ %114 = OpExtInst %void %1 DebugScope %50
+ %98 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %99 = OpVariable %_ptr_Function_v2float Function
+ %115 = OpExtInst %void %1 DebugNoScope
+ %100 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %70 = OpLoad %v2float %in_var_TEXCOORD2
+ %71 = OpCompositeConstruct %PS_INPUT %70
+ OpStore %param_var_i %71
+ %116 = OpExtInst %void %1 DebugScope %48
+ %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55
+ %117 = OpExtInst %void %1 DebugScope %50
+ %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55
+ OpLine %20 19 17
+ %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %105 = OpLoad %v2float %104
+ OpLine %20 19 12
+ OpStore %99 %105
+ %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK-NOT: %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK: %119 = OpExtInst %void %1 DebugValue %52 %105 %55
+ OpLine %20 20 26
+ %107 = OpLoad %type_2d_image %g_tColor
+ OpLine %20 20 46
+ %108 = OpLoad %type_sampler %g_sAniso
+ OpLine %20 20 57
+ %109 = OpLoad %v2float %99
+;CHECK-NOT: %109 = OpLoad %v2float %99
+ OpLine %20 20 26
+ %110 = OpSampledImage %type_sampled_image %107 %108
+ %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK-NOT: %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK: %111 = OpImageSampleImplicitLod %v4float %110 %105 None
+ OpLine %20 20 5
+ %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0
+ OpStore %112 %111
+ OpLine %20 21 12
+ %113 = OpLoad %PS_OUTPUT %98
+ OpLine %20 21 5
+ OpStore %100 %113
+ %118 = OpExtInst %void %1 DebugNoScope
+ %73 = OpLoad %PS_OUTPUT %100
+ %74 = OpCompositeExtract %v4float %73 0
+ OpStore %out_var_SV_Target0 %74
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
+TEST_F(LocalSingleStoreElimTest, DebugValueTest) {
+ // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+ // optimized, DebugValue placed after the store and the associated
+ // DebugValue Deref is removed.
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+ OpExecutionMode %MainPs OriginUpperLeft
+ %7 = OpString "foo.frag"
+ %8 = OpString "PS_OUTPUT"
+ %9 = OpString "float"
+ %10 = OpString "vColor"
+ %11 = OpString "PS_INPUT"
+ %12 = OpString "vTextureCoords"
+ %13 = OpString "@type.2d.image"
+ %14 = OpString "type.2d.image"
+ %15 = OpString "Texture2D.TemplateParam"
+ %16 = OpString "src.MainPs"
+ %17 = OpString "tc"
+ %18 = OpString "ps_output"
+ %19 = OpString "i"
+ %20 = OpString "@type.sampler"
+ %21 = OpString "type.sampler"
+ %22 = OpString "g_sAniso"
+ %23 = OpString "g_tColor"
+ OpName %type_2d_image "type.2d.image"
+ OpName %g_tColor "g_tColor"
+ OpName %type_sampler "type.sampler"
+ OpName %g_sAniso "g_sAniso"
+ OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+ OpName %out_var_SV_Target0 "out.var.SV_Target0"
+ OpName %MainPs "MainPs"
+ OpName %PS_INPUT "PS_INPUT"
+ OpMemberName %PS_INPUT 0 "vTextureCoords"
+ OpName %param_var_i "param.var.i"
+ OpName %PS_OUTPUT "PS_OUTPUT"
+ OpMemberName %PS_OUTPUT 0 "vColor"
+ OpName %type_sampled_image "type.sampled.image"
+ OpDecorate %in_var_TEXCOORD2 Location 0
+ OpDecorate %out_var_SV_Target0 Location 0
+ OpDecorate %g_tColor DescriptorSet 0
+ OpDecorate %g_tColor Binding 0
+ OpDecorate %g_sAniso DescriptorSet 0
+ OpDecorate %g_sAniso Binding 1
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+ %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %uint_64 = OpConstant %uint 64
+ %45 = OpTypeFunction %void
+ %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+ %PS_OUTPUT = OpTypeStruct %v4float
+ %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+ %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+ %51 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %53 = OpExtInst %void %1 DebugOperation Deref
+ %54 = OpExtInst %void %1 DebugExpression %53
+ %55 = OpExtInst %void %1 DebugSource %7
+ %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+ %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+ %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+ %60 = OpExtInst %void %1 DebugTypeVector %59 4
+ %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+ %63 = OpExtInst %void %1 DebugTypeVector %59 2
+ %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+ %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+ %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+ %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+ %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+ %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+ %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+ %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+ %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+ %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+ %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+ %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+ %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+ %MainPs = OpFunction %void None %45
+ %76 = OpLabel
+ %101 = OpExtInst %void %1 DebugScope %69
+ %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+ %79 = OpVariable %_ptr_Function_v2float Function
+ %102 = OpExtInst %void %1 DebugNoScope
+ %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+ %82 = OpLoad %v2float %in_var_TEXCOORD2
+ %83 = OpCompositeConstruct %PS_INPUT %82
+ OpStore %param_var_i %83
+ %103 = OpExtInst %void %1 DebugScope %68
+ %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+ %104 = OpExtInst %void %1 DebugScope %69
+ %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+ OpLine %7 19 17
+ %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+ %89 = OpLoad %v2float %88
+ OpLine %7 19 12
+ OpStore %79 %89
+ %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK-NOT: %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK: %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+ OpLine %7 20 26
+ %91 = OpLoad %type_2d_image %g_tColor
+ OpLine %7 20 46
+ %92 = OpLoad %type_sampler %g_sAniso
+ OpLine %7 20 57
+ %93 = OpLoad %v2float %79
+;CHECK-NOT: %93 = OpLoad %v2float %79
+ OpLine %7 20 26
+ %94 = OpSampledImage %type_sampled_image %91 %92
+ %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK-NOT: %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK: %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+ OpLine %7 20 5
+ %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+ OpStore %96 %95
+ OpLine %7 21 12
+ %97 = OpLoad %PS_OUTPUT %78
+ OpLine %7 21 5
+ OpStore %81 %97
+ %105 = OpExtInst %void %1 DebugNoScope
+ %99 = OpLoad %PS_OUTPUT %81
+ %100 = OpCompositeExtract %v4float %99 0
+ OpStore %out_var_SV_Target0 %100
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other types
diff --git a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
index d29a554..6581ebf 100644
--- a/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_ssa_elim_test.cpp
@@ -2024,6 +2024,1344 @@
SinglePassRunToBinary<SSARewritePass>(text, false);
}
+TEST_F(LocalSSAElimTest, DebugForLoop) {
+ // #version 140
+ //
+ // in vec4 BC;
+ // out float fo;
+ //
+ // void main()
+ // {
+ // float f = 0.0;
+ // for (int i=0; i<4; i++) {
+ // f = f + BC[i];
+ // }
+ // fo = f;
+ // }
+
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT: DebugDeclare
+
+; CHECK: [[loop_head:%\w+]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK-NEXT: [[loop_body]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK: [[bb]] = OpLabel
+; CHECK: OpStore %f [[f_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-NEXT: OpBranch [[loop_cont]]
+
+; CHECK: [[loop_cont]] = OpLabel
+; CHECK: OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK: [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueForReferenceVariable) {
+ // #version 140
+ //
+ // in vec4 BC;
+ // out float fo;
+ //
+ // void main()
+ // {
+ // float f = 0.0;
+ // float& x = f;
+ // for (int i=0; i<4; i++) {
+ // x = x + BC[i];
+ // }
+ // fo = f;
+ // }
+
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[x_name:%\w+]] = OpString "x"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]]
+
+; CHECK: OpStore %f %float_0
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0
+; CHECK: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT: DebugDeclare
+
+; CHECK: [[loop_head:%\w+]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[phi0]]
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK: [[loop_body]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK: [[bb]] = OpLabel
+; CHECK: OpStore %f [[f_val:%\w+]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]]
+; CHECK: OpBranch [[loop_cont]]
+
+; CHECK: [[loop_cont]] = OpLabel
+; CHECK: OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK: [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+%x_name = OpString "x"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueForReferenceVariableInBB) {
+ // #version 140
+ //
+ // in vec4 BC;
+ // out float fo;
+ //
+ // void main()
+ // {
+ // float f = 0.0;
+ // for (int i=0; i<4; i++) {
+ // float& x = f;
+ // x = x + BC[i];
+ // {
+ // x = x + BC[i];
+ // }
+ // }
+ // fo = f;
+ // }
+
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[x_name:%\w+]] = OpString "x"
+; CHECK: [[dbg_main:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugFunction
+; CHECK: [[dbg_bb:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock
+; CHECK: [[dbg_bb_child:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]]
+
+; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
+
+; CHECK-NOT: DebugDeclare
+
+; CHECK: [[loop_head:%\w+]] = OpLabel
+; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]]
+; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]]
+; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK-NEXT: [[loop_body]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK: [[bb]] = OpLabel
+; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_bb]]
+; CHECK: OpStore %f [[f_val:%\w+]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]]
+; CHECK: OpBranch [[bb_child:%\w+]]
+
+; CHECK: [[bb_child]] = OpLabel
+; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_bb_child]]
+; CHECK: OpStore %f [[new_f_val:%\w+]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[new_f_val]]
+; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[new_f_val]]
+; CHECK: OpBranch [[loop_cont]]
+
+; CHECK: [[loop_cont]] = OpLabel
+; CHECK: OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]]
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+; CHECK: [[loop_merge]] = OpLabel
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+%x_name = OpString "x"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%bb = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main
+%bb_child = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %bb
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %bb FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%scope = OpExtInst %void %ext DebugScope %bb
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %38
+%38 = OpLabel
+%child_scope = OpExtInst %void %ext DebugScope %bb_child
+%39 = OpLoad %float %f
+%40 = OpFAdd %float %39 %33
+OpStore %f %40
+OpBranch %25
+%25 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugForLoopUseDebugValueInsteadOfDebugDeclare) {
+ // #version 140
+ //
+ // in vec4 BC;
+ // out float fo;
+ //
+ // struct S {
+ // float f;
+ // int i;
+ // };
+ //
+ // void main()
+ // {
+ // S foo = {0.0, 0};
+ // for (; foo.i<4; foo.i++) {
+ // foo.f = foo.f + BC[foo.i];
+ // }
+ // fo = foo.f;
+ // }
+
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[empty_expr:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugExpression
+; CHECK: [[deref_op:%\w+]] = OpExtInst %void [[ext]] DebugOperation Deref
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref_op]]
+; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]]
+
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %float_0 [[empty_expr]] %uint_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %int_0 [[empty_expr]] %uint_1
+
+; CHECK: [[loop_head:%\w+]] = OpLabel
+; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0
+; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi0]] [[empty_expr]] %uint_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi1]] [[empty_expr]] %uint_1
+; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None
+; CHECK-NEXT: OpBranch [[loop_body:%\w+]]
+
+; CHECK: [[loop_body]] = OpLabel
+; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]]
+
+; CHECK: [[bb]] = OpLabel
+; CHECK: OpStore %f [[f_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[f_val]] [[empty_expr]] %uint_0
+; CHECK-NEXT: OpBranch [[loop_cont]]
+
+; CHECK: [[loop_cont]] = OpLabel
+; CHECK: OpStore %i [[i_val:%\w+]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[i_val]] [[empty_expr]] %uint_1
+; CHECK-NEXT: OpBranch [[loop_head]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%deref_op = OpExtInst %void %ext DebugOperation Deref
+%deref = OpExtInst %void %ext DebugExpression %deref_op
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_foo = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %f %deref %uint_0
+%decl1 = OpExtInst %void %ext DebugValue %dbg_foo %i %deref %uint_1
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugValueNotUsedForDebugDeclare) {
+ // #version 140
+ //
+ // in vec4 BC;
+ // out float fo;
+ //
+ // void main()
+ // {
+ // float f = 0.0;
+ // for (int i=0; i<4; i++) {
+ // f = f + BC[i];
+ // }
+ // fo = f;
+ // }
+
+ const std::string text = R"(
+; CHECK: [[f_name:%\w+]] = OpString "f"
+; CHECK: [[i_name:%\w+]] = OpString "i"
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]]
+; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]]
+
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: OpStore %i %int_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %f
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %i
+
+; CHECK-NOT: DebugValue
+; CHECK-NOT: DebugDeclare
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+OpSource GLSL 140
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+%i_name = OpString "i"
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %8
+%22 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+OpStore %f %float_0
+OpStore %i %int_0
+%decl0 = OpExtInst %void %ext DebugValue %dbg_f %f %null_expr
+%decl1 = OpExtInst %void %ext DebugValue %dbg_i %i %null_expr
+OpBranch %23
+%23 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %24 %25 None
+OpBranch %26
+%26 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%27 = OpLoad %int %i
+%28 = OpSLessThan %bool %27 %int_4
+OpBranchConditional %28 %29 %24
+%29 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %float %f
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+%34 = OpFAdd %float %30 %33
+OpStore %f %34
+OpBranch %25
+%25 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %int %i
+%36 = OpIAdd %int %35 %int_1
+OpStore %i %36
+OpBranch %23
+%24 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %float %f
+OpStore %fo %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugNestedForLoop) {
+ const std::string text = R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0
+
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]]
+
+; CHECK: [[inner_pre_header:%\w+]] = OpLabel
+; CHECK: [[inner_header:%\w+]] = OpLabel
+; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]]
+
+; CHECK: [[inner_be]] = OpLabel
+; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
+; CHECK-NEXT: OpStore %f [[f_next]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSource GLSL 450
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %j "j"
+OpName %BC "BC"
+OpName %fo "fo"
+OpDecorate %BC Location 0
+OpDecorate %fo Location 0
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%mat4v4float = OpTypeMatrix %v4float 4
+%_ptr_Input_mat4v4float = OpTypePointer Input %mat4v4float
+%BC = OpVariable %_ptr_Input_mat4v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%j = OpVariable %_ptr_Function_int Function
+
+; DebugDeclare
+OpStore %f %float_0
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+
+OpStore %i %int_0
+OpBranch %25
+%25 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%26 = OpLoad %int %i
+%27 = OpSLessThan %bool %26 %int_4
+OpLoopMerge %28 %29 None
+OpBranchConditional %27 %30 %28
+%30 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+OpStore %j %int_0
+OpBranch %31
+%31 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%32 = OpLoad %int %j
+%33 = OpSLessThan %bool %32 %int_4
+OpLoopMerge %50 %34 None
+OpBranchConditional %33 %34 %50
+%34 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %float %f
+%36 = OpLoad %int %i
+%37 = OpLoad %int %j
+%38 = OpAccessChain %_ptr_Input_float %BC %36 %37
+%39 = OpLoad %float %38
+%40 = OpFAdd %float %35 %39
+OpStore %f %40
+%41 = OpLoad %int %j
+%42 = OpIAdd %int %41 %int_1
+OpStore %j %42
+OpBranch %31
+%50 = OpLabel
+%s6 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %29
+%29 = OpLabel
+%s7 = OpExtInst %void %ext DebugScope %dbg_main
+%43 = OpLoad %int %i
+%44 = OpIAdd %int %43 %int_1
+OpStore %i %44
+OpBranch %25
+%28 = OpLabel
+%s8 = OpExtInst %void %ext DebugScope %dbg_main
+%45 = OpLoad %float %f
+OpStore %fo %45
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugForLoopWithContinue) {
+ const std::string text = R"(
+; CHECK: = OpFunction
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %f %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0
+
+; CHECK: [[outer_header:%\w+]] = OpLabel
+; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[cont:%\w+]]
+; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]]
+
+; CHECK: [[f_next:%\w+]] = OpFAdd %float [[outer_f]]
+; CHECK-NEXT: OpStore %f [[f_next]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]]
+
+; CHECK: [[cont]] = OpLabel
+; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] {{%\d+}} [[f_next]] {{%\d+}}
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]]
+
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BC %fo
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSource GLSL 140
+OpName %main "main"
+OpName %f "f"
+OpName %i "i"
+OpName %t "t"
+OpName %BC "BC"
+OpName %fo "fo"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BC = OpVariable %_ptr_Input_v4float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%23 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f = OpVariable %_ptr_Function_float Function
+%i = OpVariable %_ptr_Function_int Function
+%t = OpVariable %_ptr_Function_float Function
+
+; DebugDeclare
+OpStore %f %float_0
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr
+
+OpStore %i %int_0
+OpBranch %24
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %25 %26 None
+OpBranch %27
+%27 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%28 = OpLoad %int %i
+%29 = OpSLessThan %bool %28 %int_4
+OpBranchConditional %29 %30 %25
+%30 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%31 = OpLoad %int %i
+%32 = OpAccessChain %_ptr_Input_float %BC %31
+%33 = OpLoad %float %32
+OpStore %t %33
+%34 = OpLoad %float %t
+%35 = OpFOrdLessThan %bool %34 %float_0
+OpSelectionMerge %36 None
+OpBranchConditional %35 %37 %36
+%37 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %26
+%36 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%38 = OpLoad %float %f
+%39 = OpLoad %float %t
+%40 = OpFAdd %float %38 %39
+OpStore %f %40
+OpBranch %26
+%26 = OpLabel
+%s6 = OpExtInst %void %ext DebugScope %dbg_main
+%41 = OpLoad %int %i
+%42 = OpIAdd %int %41 %int_1
+OpStore %i %42
+OpBranch %24
+%25 = OpLabel
+%s7 = OpExtInst %void %ext DebugScope %dbg_main
+%43 = OpLoad %float %f
+OpStore %fo %43
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugIfElse) {
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %f %BaseColor %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%v_name = OpString "v"
+OpSource GLSL 140
+OpName %main "main"
+OpName %f "f"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+%f = OpVariable %_ptr_Input_float Input
+%float_0 = OpConstant %float 0
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%bool = OpTypeBool
+%v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%float_0_5 = OpConstant %float 0.5
+%float_1 = OpConstant %float 1
+%18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %8
+%20 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+
+; DebugDeclare
+%v = OpVariable %_ptr_Function_v4float Function
+%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr
+
+%21 = OpLoad %float %f
+%22 = OpFOrdGreaterThanEqual %bool %21 %float_0
+OpSelectionMerge %23 None
+OpBranchConditional %22 %24 %25
+
+; CHECK: OpBranchConditional
+; CHECK-NEXT: [[br0:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v0:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]]
+%24 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%26 = OpLoad %v4float %BaseColor
+%27 = OpVectorTimesScalar %v4float %26 %float_0_5
+OpStore %v %27
+OpBranch %23
+
+; CHECK: [[br1:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v1:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]]
+%25 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%28 = OpLoad %v4float %BaseColor
+%29 = OpFAdd %v4float %28 %18
+OpStore %v %29
+OpBranch %23
+
+; CHECK: [[phi:%\w+]] = OpPhi %v4float [[v0]] [[br0]] [[v1]] [[br1]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi]]
+%23 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %v4float %v
+OpStore %gl_FragColor %30
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugSwitch) {
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor
+OpExecutionMode %main OriginUpperLeft
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%v_name = OpString "v"
+OpSource GLSL 140
+OpName %main "main"
+OpName %v "v"
+OpName %BaseColor "BaseColor"
+OpName %i "i"
+OpName %f "f"
+OpName %gl_FragColor "gl_FragColor"
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%BaseColor = OpVariable %_ptr_Input_v4float Input
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Input_float = OpTypePointer Input %float
+%f = OpVariable %_ptr_Input_float Input
+%float_0_25 = OpConstant %float 0.25
+%float_0_75 = OpConstant %float 0.75
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%gl_FragColor = OpVariable %_ptr_Output_v4float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %9
+%20 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%v = OpVariable %_ptr_Function_v4float Function
+%i = OpVariable %_ptr_Function_int Function
+%21 = OpLoad %v4float %BaseColor
+
+; DebugDeclare
+OpStore %v %21
+%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr
+
+; CHECK: %main = OpFunction %void None
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+; CHECK: OpStore %v [[v0:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]]
+; CHECK: OpSwitch {{%\w+}} [[case0:%\w+]] 0 [[case1:%\w+]] 1 [[case2:%\w+]] 2 [[case3:%\w+]]
+; CHECK: OpStore %v [[v1:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]]
+; CHECK: OpStore %v [[v2:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v2]]
+; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[v0]] [[entry]] [[v2]] [[case2]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi0]]
+; CHECK: OpStore %v [[v3:%\w+]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v3]]
+; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[v0]] [[case0]] [[v1]] [[case1]] [[v3]] [[case3]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi1]]
+
+%22 = OpLoad %float %f
+%23 = OpConvertFToS %int %22
+OpStore %i %23
+%24 = OpLoad %int %i
+OpSelectionMerge %25 None
+OpSwitch %24 %26 0 %27 1 %28 2 %29
+%26 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpBranch %25
+%27 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %v4float %v
+%31 = OpVectorTimesScalar %v4float %30 %float_0_25
+OpStore %v %31
+OpBranch %25
+%28 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%32 = OpLoad %v4float %v
+%33 = OpCompositeConstruct %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25
+%34 = OpFAdd %v4float %32 %33
+OpStore %v %34
+OpBranch %29
+%29 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%35 = OpLoad %v4float %v
+%36 = OpVectorTimesScalar %v4float %35 %float_0_75
+OpStore %v %36
+OpBranch %25
+%25 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %v4float %v
+OpStore %gl_FragColor %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
+TEST_F(LocalSSAElimTest, DebugSwapProblem) {
+ // #version 140
+ //
+ // in float fe;
+ // out float fo;
+ //
+ // void main()
+ // {
+ // float f1 = 0.0;
+ // float f2 = 1.0;
+ // int ie = int(fe);
+ // for (int i=0; i<ie; i++) {
+ // float t = f1;
+ // f1 = f2;
+ // f2 = t;
+ // }
+ // fo = f1;
+ // }
+ //
+ // Because of the swap in the for loop, it generates the following phi
+ // instructions:
+ //
+ // [[phi_f2]] = OpPhi %float %float_1 [[entry]] [[phi_f1]] ..
+ // [[phi_f1]] = OpPhi %float %float_0 [[entry]] [[phi_f2]] ..
+ //
+ // Since they are used as operands by each other, we want to clearly check
+ // what DebugValue we have to add for them.
+
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %fe %fo
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%t_name = OpString "t"
+OpName %main "main"
+OpName %f1 "f1"
+OpName %f2 "f2"
+OpName %ie "ie"
+OpName %fe "fe"
+OpName %i "i"
+OpName %t "t"
+OpName %fo "fo"
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%_ptr_Input_float = OpTypePointer Input %float
+%fe = OpVariable %_ptr_Input_float Input
+%int_0 = OpConstant %int 0
+%bool = OpTypeBool
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Output_float = OpTypePointer Output %float
+%fo = OpVariable %_ptr_Output_float Output
+
+; Debug information
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f1 = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%dbg_f2 = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%dbg_i = OpExtInst %void %ext DebugLocalVariable %t_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+%main = OpFunction %void None %11
+%23 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%f1 = OpVariable %_ptr_Function_float Function
+%f2 = OpVariable %_ptr_Function_float Function
+%ie = OpVariable %_ptr_Function_int Function
+%i = OpVariable %_ptr_Function_int Function
+%t = OpVariable %_ptr_Function_float Function
+OpStore %f1 %float_0
+OpStore %f2 %float_1
+%24 = OpLoad %float %fe
+%25 = OpConvertFToS %int %24
+OpStore %ie %25
+OpStore %i %int_0
+
+; DebugDeclare
+%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f1 %f1 %null_expr
+%decl1 = OpExtInst %void %ext DebugDeclare %dbg_f2 %f2 %null_expr
+%decl2 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr
+
+; CHECK: %main = OpFunction %void None
+; CHECK-NEXT: [[entry:%\w+]] = OpLabel
+
+; CHECK: OpStore %f1 %float_0
+; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f1:%\w+]] %float_0
+; CHECK: OpStore %f2 %float_1
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f2:%\w+]] %float_1
+; CHECK: OpStore %i %int_0
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_i:%\w+]] %int_0
+
+; CHECK: [[phi_f2:%\w+]] = OpPhi %float %float_1 [[entry]] [[phi_f1:%\w+]]
+; CHECK: [[phi_f1]] = OpPhi %float %float_0 [[entry]] [[phi_f2]]
+; CHECK: [[phi_i:%\w+]] = OpPhi %int %int_0 [[entry]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f2]] [[phi_f2]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f1]] [[phi_f1]]
+; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi_i]]
+
+OpBranch %26
+%26 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+OpLoopMerge %27 %28 None
+OpBranch %29
+%29 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%30 = OpLoad %int %i
+%31 = OpLoad %int %ie
+%32 = OpSLessThan %bool %30 %31
+OpBranchConditional %32 %33 %27
+%33 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%34 = OpLoad %float %f1
+OpStore %t %34
+%35 = OpLoad %float %f2
+OpStore %f1 %35
+%36 = OpLoad %float %t
+OpStore %f2 %36
+OpBranch %28
+%28 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpLoad %int %i
+%38 = OpIAdd %int %37 %int_1
+OpStore %i %38
+OpBranch %26
+%27 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%39 = OpLoad %float %f1
+OpStore %fo %39
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
index e513f42..55b9c26 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/loop_fission.cpp
@@ -2203,11 +2203,11 @@
%45 = OpAccessChain %18 %4 %39
OpStore %45 %44
%46 = OpIEqual %12 %39 %19
-OpSelectionMerge %47 None
-OpBranchConditional %46 %51 %47
+OpSelectionMerge %48 None
+OpBranchConditional %46 %47 %48
%47 = OpLabel
OpBranch %52
-%51 = OpLabel
+%48 = OpLabel
OpBranch %52
%52 = OpLabel
%53 = OpIAdd %8 %39 %19
@@ -2242,16 +2242,16 @@
OpReturn
OpFunctionEnd
)";
- // clang-format on
- std::unique_ptr<IRContext> context =
- BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source,
- SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
- Module* module = context->module();
- EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
- << source << std::endl;
+// clang-format on
+std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+Module* module = context->module();
+EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
+ << source << std::endl;
- SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
- SinglePassRunAndCheck<LoopFissionPass>(source, expected, true);
+SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+SinglePassRunAndCheck<LoopFissionPass>(source, expected, true);
}
/*
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
index 62f77d7..0f93302 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_assumptions.cpp
@@ -467,6 +467,73 @@
SinglePassRunAndCheck<LoopUnroller>(text, text, false);
}
+TEST_F(PassClassTest, KillInBody) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpTypeBool
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+%7 = OpConstant %5 1
+%8 = OpConstant %5 5
+%1 = OpFunction %2 None %3
+%9 = OpLabel
+OpBranch %10
+%10 = OpLabel
+%11 = OpPhi %5 %6 %9 %12 %13
+%14 = OpULessThan %4 %11 %8
+OpLoopMerge %15 %13 Unroll
+OpBranchConditional %14 %16 %15
+%16 = OpLabel
+OpKill
+%13 = OpLabel
+%12 = OpIAdd %5 %11 %7
+OpBranch %10
+%15 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
+TEST_F(PassClassTest, TerminateInvocationInBody) {
+ const std::string text = R"(OpCapability Shader
+OpExtension "SPV_KHR_terminate_invocation"
+OpMemoryModel Logical Simple
+OpEntryPoint Fragment %1 "main"
+OpExecutionMode %1 OriginUpperLeft
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%4 = OpTypeBool
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 0
+%7 = OpConstant %5 1
+%8 = OpConstant %5 5
+%1 = OpFunction %2 None %3
+%9 = OpLabel
+OpBranch %10
+%10 = OpLabel
+%11 = OpPhi %5 %6 %9 %12 %13
+%14 = OpULessThan %4 %11 %8
+OpLoopMerge %15 %13 Unroll
+OpBranchConditional %14 %16 %15
+%16 = OpLabel
+OpTerminateInvocation
+%13 = OpLabel
+%12 = OpIAdd %5 %11 %7
+OpBranch %10
+%15 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ SinglePassRunAndCheck<LoopUnroller>(text, text, false);
+}
+
/*
Generated from the following GLSL
#version 440 core
diff --git a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
index f551e7c..6030135 100644
--- a/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/third_party/SPIRV-Tools/test/opt/loop_optimizations/unroll_simple.cpp
@@ -2998,6 +2998,76 @@
kUnrollFactor);
}
+// Test that a loop containing an unreachable merge block can still be unrolled
+// correctly.
+TEST_F(PassClassTest, UnreachableMerge) {
+ const std::string text = R"(
+; Identify the first iteration of the unrolled loop, and make sure it contains
+; the unreachable merge block.
+; The first SelectionMerge corresponds to the original loop merge.
+; The second is the branch in the loop.
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpSelectionMerge [[unrch1:%\w+]] None
+; CHECK: [[unrch1]] = OpLabel
+; CHECK-NEXT: OpUnreachable
+; Identify the second iteration of the unrolled loop, and make sure it contains
+; the unreachable merge block.
+; The first SelectionMerge corresponds to the original loop merge
+; The second is the branch in the loop.
+; CHECK: OpSelectionMerge {{%\w+}} None
+; CHECK: OpSelectionMerge [[unrch2:%\w+]] None
+; CHECK: [[unrch2]] = OpLabel
+; CHECK-NEXT: OpUnreachable
+
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 64 1 1
+ OpSource HLSL 600
+ OpName %main "main"
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %uint_2 = OpConstant %uint 2
+ %uint_1 = OpConstant %uint 1
+ %bool = OpTypeBool
+ %void = OpTypeVoid
+ %18 = OpTypeFunction %void
+ %main = OpFunction %void None %18
+ %23 = OpLabel
+ OpBranch %24
+ %24 = OpLabel
+ %28 = OpPhi %uint %uint_0 %23 %29 %27
+ %30 = OpULessThan %bool %28 %uint_2
+ OpLoopMerge %31 %27 Unroll
+ OpBranchConditional %30 %32 %31
+ %32 = OpLabel
+ OpSelectionMerge %33 None
+ OpSwitch %uint_0 %34
+ %34 = OpLabel
+ %35 = OpUndef %bool
+ OpSelectionMerge %36 None
+ OpBranchConditional %35 %37 %38
+ %38 = OpLabel
+ OpBranch %33
+ %37 = OpLabel
+ OpBranch %33
+ %36 = OpLabel
+ OpUnreachable
+ %33 = OpLabel
+ OpBranch %27
+ %27 = OpLabel
+ %29 = OpIAdd %uint %28 %uint_1
+ OpBranch %24
+ %31 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const bool kFullyUnroll = true;
+ const uint32_t kUnrollFactor = 0;
+ SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll,
+ kUnrollFactor);
+}
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
index e55a48f..f426904 100644
--- a/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/pass_merge_return_test.cpp
@@ -99,6 +99,78 @@
SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
}
+TEST_F(MergeReturnPassTest, DebugTwoReturnsNoValue) {
+ const std::string before =
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpCapability GenericPointer
+OpCapability Linkage
+%10 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %6 "simple_kernel"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%12 = OpExtInst %2 %10 DebugSource %11
+%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL
+%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2
+%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%16 = OpExtInst %2 %10 DebugScope %15
+OpLine %11 100 0
+OpReturn
+%9 = OpLabel
+%17 = OpExtInst %2 %10 DebugScope %13
+OpLine %11 200 0
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpCapability GenericPointer
+OpCapability Linkage
+%10 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %6 "simple_kernel"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%12 = OpExtInst %2 %10 DebugSource %11
+%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL
+%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2
+%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%19 = OpExtInst %2 %10 DebugScope %15
+OpLine %11 100 0
+OpBranch %18
+%20 = OpExtInst %2 %10 DebugNoScope
+%9 = OpLabel
+%21 = OpExtInst %2 %10 DebugScope %13
+OpLine %11 200 0
+OpBranch %18
+%22 = OpExtInst %2 %10 DebugNoScope
+%18 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
+}
+
TEST_F(MergeReturnPassTest, TwoReturnsWithValues) {
const std::string before =
R"(OpCapability Linkage
@@ -261,6 +333,92 @@
SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
}
+TEST_F(MergeReturnPassTest, DebugUnreachableReturnsWithValues) {
+ const std::string before =
+ R"(OpCapability Linkage
+OpCapability Kernel
+%13 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical OpenCL
+%14 = OpString "test"
+OpDecorate %7 LinkageAttributes "simple_kernel" Export
+%1 = OpTypeInt 32 0
+%20 = OpTypeVoid
+%2 = OpTypeBool
+%3 = OpConstantFalse %2
+%4 = OpConstant %1 0
+%5 = OpConstant %1 1
+%6 = OpTypeFunction %1
+%15 = OpExtInst %20 %13 DebugSource %14
+%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL
+%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20
+%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7
+%7 = OpFunction %1 None %6
+%8 = OpLabel
+%9 = OpIAdd %1 %4 %5
+%19 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 100 0
+OpReturnValue %9
+%10 = OpLabel
+OpBranchConditional %3 %11 %12
+%11 = OpLabel
+%21 = OpExtInst %20 %13 DebugScope %16
+OpLine %14 200 0
+OpReturnValue %4
+%12 = OpLabel
+%22 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 300 0
+OpReturnValue %5
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(OpCapability Linkage
+OpCapability Kernel
+%13 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical OpenCL
+%14 = OpString "test"
+OpDecorate %7 LinkageAttributes "simple_kernel" Export
+%1 = OpTypeInt 32 0
+%20 = OpTypeVoid
+%2 = OpTypeBool
+%3 = OpConstantFalse %2
+%4 = OpConstant %1 0
+%5 = OpConstant %1 1
+%6 = OpTypeFunction %1
+%15 = OpExtInst %20 %13 DebugSource %14
+%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL
+%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20
+%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7
+%7 = OpFunction %1 None %6
+%8 = OpLabel
+%9 = OpIAdd %1 %4 %5
+%25 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 100 0
+OpBranch %23
+%26 = OpExtInst %20 %13 DebugNoScope
+%10 = OpLabel
+OpBranchConditional %3 %11 %12
+%11 = OpLabel
+%27 = OpExtInst %20 %13 DebugScope %16
+OpLine %14 200 0
+OpBranch %23
+%28 = OpExtInst %20 %13 DebugNoScope
+%12 = OpLabel
+%29 = OpExtInst %20 %13 DebugScope %18
+OpLine %14 300 0
+OpBranch %23
+%30 = OpExtInst %20 %13 DebugNoScope
+%23 = OpLabel
+%24 = OpPhi %1 %9 %8 %4 %11 %5 %12
+OpReturnValue %24
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ SinglePassRunAndCheck<MergeReturnPass>(before, after, false, true);
+}
+
TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableMerge) {
const std::string before =
R"(
@@ -307,6 +465,68 @@
SinglePassRunAndMatch<MergeReturnPass>(before, false);
}
+TEST_F(MergeReturnPassTest, DebugStructuredControlFlowWithUnreachableMerge) {
+ const std::string before =
+ R"(
+; CHECK: [[false:%\w+]] = OpConstantFalse
+; CHECK: [[true:%\w+]] = OpConstantTrue
+; CHECK: OpFunction
+; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]]
+; CHECK: OpSelectionMerge [[return_block:%\w+]]
+; CHECK: OpSelectionMerge [[merge_lab:%\w+]]
+; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]]
+; CHECK: [[if_lab]] = OpLabel
+; CHECK-NEXT: OpStore [[var]] [[true]]
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 100 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[then_lab]] = OpLabel
+; CHECK-NEXT: OpStore [[var]] [[true]]
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 200 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[merge_lab]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 300 0
+; CHECK-NEXT: OpBranch [[return_block]]
+; CHECK: [[return_block]] = OpLabel
+; CHECK-NEXT: OpReturn
+OpCapability Addresses
+OpCapability Shader
+OpCapability Linkage
+%12 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %6 "simple_shader"
+%11 = OpString "test"
+%2 = OpTypeVoid
+%3 = OpTypeBool
+%4 = OpConstantFalse %3
+%1 = OpTypeFunction %2
+%13 = OpExtInst %2 %12 DebugSource %11
+%14 = OpExtInst %2 %12 DebugCompilationUnit 1 4 %13 HLSL
+%6 = OpFunction %2 None %1
+%7 = OpLabel
+OpSelectionMerge %10 None
+OpBranchConditional %4 %8 %9
+%8 = OpLabel
+%15 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 100 0
+OpReturn
+%9 = OpLabel
+%16 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 200 0
+OpReturn
+%10 = OpLabel
+%17 = OpExtInst %2 %12 DebugScope %14
+OpLine %11 300 0
+OpUnreachable
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<MergeReturnPass>(before, false);
+}
+
TEST_F(MergeReturnPassTest, StructuredControlFlowAddPhi) {
const std::string before =
R"(
@@ -451,6 +671,89 @@
SinglePassRunAndMatch<MergeReturnPass>(before, false);
}
+TEST_F(MergeReturnPassTest, DebugSplitBlockUsedInPhi) {
+ const std::string before =
+ R"(
+; CHECK: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 100 0
+; CHECK: OpLoopMerge
+
+; CHECK: OpStore [[return_in_loop:%\w+]] %true
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 200 0
+; CHECK-NEXT: OpBranch [[check_early_return:%\w+]]
+
+; CHECK: [[check_early_return]] = OpLabel
+; CHECK-NEXT: [[early_return:%\w+]] = OpLoad %bool [[return_in_loop]]
+; CHECK-NEXT: OpSelectionMerge [[not_early_return:%\w+]] None
+; CHECK-NEXT: OpBranchConditional [[early_return]] {{%\d+}} [[not_early_return]]
+
+; CHECK: [[not_early_return]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 400 0
+; CHECK: OpSelectionMerge [[merge2:%\w+]] None
+
+; CHECK: [[merge2]] = OpLabel
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: OpLine {{%\d+}} 600 0
+; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %false {{%\d+}} %true [[not_early_return]]
+; CHECK-NEXT: DebugValue {{%\d+}} [[phi]]
+
+ OpCapability Addresses
+ OpCapability Shader
+ OpCapability Linkage
+ %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "simple_shader"
+ %tn = OpString "test"
+ %void = OpTypeVoid
+ %bool = OpTypeBool
+ %uint = OpTypeInt 32 0
+ %uint_8 = OpConstant %uint 8
+ %false = OpConstantFalse %bool
+ %true = OpConstantTrue %bool
+ %6 = OpTypeFunction %void
+ %src = OpExtInst %void %ext DebugSource %tn
+ %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+ %ty = OpExtInst %void %ext DebugTypeBasic %tn %uint_8 Boolean
+ %v = OpExtInst %void %ext DebugLocalVariable %tn %ty %src 0 0 %cu FlagIsLocal
+ %expr = OpExtInst %void %ext DebugExpression
+ %1 = OpFunction %void None %6
+ %7 = OpLabel
+ %s0 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 100 0
+ OpLoopMerge %merge %cont None
+ OpBranchConditional %false %9 %merge
+ %9 = OpLabel
+ %s1 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 200 0
+ OpReturn
+ %cont = OpLabel
+ %s2 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 300 0
+ OpBranch %7
+ %merge = OpLabel
+ %s3 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 400 0
+ OpSelectionMerge %merge2 None
+ OpBranchConditional %false %if %merge2
+ %if = OpLabel
+ %s4 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 500 0
+ OpBranch %merge2
+ %merge2 = OpLabel
+ %s5 = OpExtInst %void %ext DebugScope %cu
+ OpLine %tn 600 0
+ %12 = OpPhi %bool %false %if %true %merge
+ %dv = OpExtInst %void %ext DebugValue %v %12 %expr
+ OpLine %tn 900 0
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<MergeReturnPass>(before, false);
+}
+
// TODO(#1861): Reenable these test when the breaks from selection constructs
// are reenabled.
/*
@@ -2077,6 +2380,193 @@
SinglePassRunAndMatch<MergeReturnPass>(before, true);
}
+TEST_F(MergeReturnPassTest, PointerUsedAfterLoop) {
+ // Make sure that a Phi instruction is not generated for an id whose type is a
+ // pointer. It needs to be regenerated.
+ const std::string before =
+ R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1
+; CHECK: OpStore [[ac]] %uint_1
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+ %8 = OpTypeFunction %void %_ptr_Function_v2uint
+ %uint_1 = OpConstant %uint 1
+ %bool = OpTypeBool
+%_ptr_Function_uint = OpTypePointer Function %uint
+ %false = OpConstantFalse %bool
+ %2 = OpFunction %void None %4
+ %13 = OpLabel
+ %14 = OpVariable %_ptr_Function_v2uint Function
+ %15 = OpFunctionCall %void %16 %14
+ OpReturn
+ OpFunctionEnd
+ %16 = OpFunction %void None %8
+ %17 = OpFunctionParameter %_ptr_Function_v2uint
+ %18 = OpLabel
+ OpBranch %19
+ %19 = OpLabel
+ OpLoopMerge %20 %21 None
+ OpBranch %22
+ %22 = OpLabel
+ OpSelectionMerge %23 None
+ OpBranchConditional %false %24 %23
+ %24 = OpLabel
+ OpReturn
+ %23 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1
+ OpBranchConditional %false %19 %20
+ %20 = OpLabel
+ OpStore %25 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
+TEST_F(MergeReturnPassTest, VariablePointerFunctionScope) {
+ // Make sure that a Phi instruction is not generated for an id whose type is a
+ // function scope pointer, even if the VariablePointers capability is
+ // available. It needs to be regenerated.
+ const std::string before =
+ R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1
+; CHECK: OpStore [[ac]] %uint_1
+ OpCapability Shader
+ OpCapability VariablePointers
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+ %8 = OpTypeFunction %void %_ptr_Function_v2uint
+ %uint_1 = OpConstant %uint 1
+ %bool = OpTypeBool
+%_ptr_Function_uint = OpTypePointer Function %uint
+ %false = OpConstantFalse %bool
+ %2 = OpFunction %void None %4
+ %13 = OpLabel
+ %14 = OpVariable %_ptr_Function_v2uint Function
+ %15 = OpFunctionCall %void %16 %14
+ OpReturn
+ OpFunctionEnd
+ %16 = OpFunction %void None %8
+ %17 = OpFunctionParameter %_ptr_Function_v2uint
+ %18 = OpLabel
+ OpBranch %19
+ %19 = OpLabel
+ OpLoopMerge %20 %21 None
+ OpBranch %22
+ %22 = OpLabel
+ OpSelectionMerge %23 None
+ OpBranchConditional %false %24 %23
+ %24 = OpLabel
+ OpReturn
+ %23 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1
+ OpBranchConditional %false %19 %20
+ %20 = OpLabel
+ OpStore %25 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
+TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) {
+ // Make sure that a Phi instruction is not generated for an id whose type is a
+ // pointer. It needs to be regenerated.
+ const std::string before =
+ R"(
+; CHECK: OpFunction %void
+; CHECK: OpFunction %void
+; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_
+; CHECK: OpLoopMerge [[merge_bb:%\w+]]
+; CHECK: [[merge_bb]] = OpLabel
+; CHECK-NEXT: [[ac1:%\w+]] = OpAccessChain %_ptr_Function_v2uint [[param]] %uint_1
+; CHECK-NEXT: [[ac2:%\w+]] = OpAccessChain %_ptr_Function_uint [[ac1]] %uint_1
+; CHECK: OpStore [[ac2]] %uint_1
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %v2uint = OpTypeVector %uint 2
+%_arr_v2uint_uint_2 = OpTypeArray %v2uint %uint_2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+%_ptr_Function__arr_v2uint_uint_2 = OpTypePointer Function %_arr_v2uint_uint_2
+%_ptr_Function_uint = OpTypePointer Function %uint
+ %13 = OpTypeFunction %void %_ptr_Function__arr_v2uint_uint_2
+ %bool = OpTypeBool
+ %false = OpConstantFalse %bool
+ %2 = OpFunction %void None %4
+ %16 = OpLabel
+ %17 = OpVariable %_ptr_Function__arr_v2uint_uint_2 Function
+ %18 = OpFunctionCall %void %19 %17
+ OpReturn
+ OpFunctionEnd
+ %19 = OpFunction %void None %13
+ %20 = OpFunctionParameter %_ptr_Function__arr_v2uint_uint_2
+ %21 = OpLabel
+ OpBranch %22
+ %22 = OpLabel
+ OpLoopMerge %23 %24 None
+ OpBranch %25
+ %25 = OpLabel
+ OpSelectionMerge %26 None
+ OpBranchConditional %false %27 %26
+ %27 = OpLabel
+ OpReturn
+ %26 = OpLabel
+ OpBranch %24
+ %24 = OpLabel
+ %28 = OpAccessChain %_ptr_Function_v2uint %20 %uint_1
+ %29 = OpAccessChain %_ptr_Function_uint %28 %uint_1
+ OpBranchConditional %false %22 %23
+ %23 = OpLabel
+ OpStore %29 %uint_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<MergeReturnPass>(before, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp b/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
index 1230652..8b5ec59 100644
--- a/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/private_to_local_test.cpp
@@ -452,6 +452,50 @@
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
}
+TEST_F(PrivateToLocalTest, DebugPrivateToLocal) {
+ // Debug instructions must not have any impact on changing the private
+ // variable to a local.
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ %10 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ %11 = OpString "test"
+ OpSource GLSL 430
+ %13 = OpTypeInt 32 0
+ %14 = OpConstant %13 32
+ %3 = OpTypeVoid
+ %4 = OpTypeFunction %3
+; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32
+ %5 = OpTypeFloat 32
+; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]]
+ %6 = OpTypePointer Private %5
+; CHECK-NOT: OpVariable [[.+]] Private
+ %8 = OpVariable %6 Private
+
+ %12 = OpExtInst %3 %10 DebugTypeBasic %11 %14 Float
+ %15 = OpExtInst %3 %10 DebugSource %11
+ %16 = OpExtInst %3 %10 DebugCompilationUnit 1 4 %15 GLSL
+; CHECK-NOT: DebugGlobalVariable
+; CHECK: [[dbg_newvar:%[a-zA-Z_\d]+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable
+ %17 = OpExtInst %3 %10 DebugGlobalVariable %11 %12 %15 0 0 %16 %11 %8 FlagIsDefinition
+
+; CHECK: OpFunction
+ %2 = OpFunction %3 None %4
+; CHECK: OpLabel
+ %7 = OpLabel
+; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function
+; CHECK-NEXT: DebugDeclare [[dbg_newvar]] [[newvar]]
+; CHECK: OpLoad [[float]] [[newvar]]
+ %9 = OpLoad %5 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+ SinglePassRunAndMatch<PrivateToLocalPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
index 50dc501..7672e8f 100644
--- a/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/reduce_load_size_test.cpp
@@ -107,6 +107,104 @@
SinglePassRunAndMatch<ReduceLoadSize>(test, false);
}
+TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) {
+ // Originally from the following HLSL:
+ // struct S {
+ // uint f;
+ // };
+ //
+ //
+ // cbuffer gBuffer { uint a[32]; };
+ //
+ // RWStructuredBuffer<S> gRWSBuffer;
+ //
+ // uint foo(uint p[32]) {
+ // return p[1];
+ // }
+ //
+ // [numthreads(1,1,1)]
+ // void main() {
+ // gRWSBuffer[0].f = foo(a);
+ // }
+ const std::string test =
+ R"(
+ OpCapability Shader
+ %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource HLSL 600
+ %file_name = OpString "test"
+ %float_name = OpString "float"
+ %main_name = OpString "main"
+ %f_name = OpString "f"
+ OpName %type_gBuffer "type.gBuffer"
+ OpMemberName %type_gBuffer 0 "a"
+ OpName %gBuffer "gBuffer"
+ OpName %S "S"
+ OpMemberName %S 0 "f"
+ OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+ OpName %gRWSBuffer "gRWSBuffer"
+ OpName %main "main"
+ OpDecorate %_arr_uint_uint_32 ArrayStride 16
+ OpMemberDecorate %type_gBuffer 0 Offset 0
+ OpDecorate %type_gBuffer Block
+ OpMemberDecorate %S 0 Offset 0
+ OpDecorate %_runtimearr_S ArrayStride 4
+ OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+ OpDecorate %type_RWStructuredBuffer_S BufferBlock
+ OpDecorate %gBuffer DescriptorSet 0
+ OpDecorate %gBuffer Binding 0
+ OpDecorate %gRWSBuffer DescriptorSet 0
+ OpDecorate %gRWSBuffer Binding 1
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%type_gBuffer = OpTypeStruct %_arr_uint_uint_32
+%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer
+ %S = OpTypeStruct %uint
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+ %int = OpTypeInt 32 1
+ %void = OpTypeVoid
+ %15 = OpTypeFunction %void
+ %int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_uint_uint_32 = OpTypePointer Uniform %_arr_uint_uint_32
+ %uint_0 = OpConstant %uint 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+ %gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform
+ %gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+ %null_expr = OpExtInst %void %ext DebugExpression
+ %src = OpExtInst %void %ext DebugSource %file_name
+ %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+ %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+ %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+ %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+ %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+ %main = OpFunction %void None %15
+ %20 = OpLabel
+ %s = OpExtInst %void %ext DebugScope %dbg_main
+; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %gBuffer %int_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[ac2]]
+; CHECK: OpStore {{%\w+}} [[ld]]
+ %21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_32 %gBuffer %int_0
+ %22 = OpLoad %_arr_uint_uint_32 %21 ; Load of 32-element array.
+ %value = OpExtInst %void %ext DebugValue %dbg_f %22 %null_expr
+ %23 = OpCompositeExtract %uint %22 1
+ %24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0
+ OpStore %24 %23
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<ReduceLoadSize>(test, false);
+}
+
TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) {
// Originally from the following HLSL:
// struct S {
diff --git a/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp b/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
index 594995c..9bdad37 100644
--- a/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/vector_dce_test.cpp
@@ -1158,6 +1158,199 @@
SinglePassRunAndCheck<VectorDCE>(text, text, true, true);
}
+TEST_F(VectorDCETest, NotAffectedByDebugValue) {
+ // It tests that an OpenCL.DebugInfo.100 DebugValue instruction does
+ // not change the vector DCE pass result. If the composite used for
+ // the value of DebugValue is killed, the DebugValue must be killed as well.
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %main "main"
+OpName %In2 "In2"
+OpName %In0 "In0"
+OpName %In1 "In1"
+OpName %OutColor "OutColor"
+OpName %_Globals_ "_Globals_"
+OpMemberName %_Globals_ 0 "g_b"
+OpMemberName %_Globals_ 1 "g_n"
+OpName %_ ""
+OpDecorate %In2 Location 2
+OpDecorate %In0 Location 0
+OpDecorate %In1 Location 1
+OpDecorate %OutColor Location 0
+OpMemberDecorate %_Globals_ 0 Offset 0
+OpMemberDecorate %_Globals_ 1 Offset 4
+OpDecorate %_Globals_ Block
+OpDecorate %_ DescriptorSet 0
+OpDecorate %_ Binding 0
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%In2 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Input_float = OpTypePointer Input %float
+%In0 = OpVariable %_ptr_Input_float Input
+%In1 = OpVariable %_ptr_Input_float Input
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%int = OpTypeInt 32 1
+%_Globals_ = OpTypeStruct %uint %int
+%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_
+%_ = OpVariable %_ptr_Uniform__Globals_ Uniform
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %11
+%25 = OpLabel
+%s = OpExtInst %void %ext DebugScope %dbg_main
+
+; CHECK: [[in2:%\w+]] = OpLoad %v2float %In2
+%26 = OpLoad %v2float %In2
+%27 = OpLoad %float %In0
+%28 = OpLoad %float %In1
+%29 = OpFAdd %float %27 %28
+
+; CHECK: OpCompositeInsert %v2float {{%\w+}} [[in2]] 0
+; CHECK-NEXT: OpCompositeInsert %v2float {{%\w+}} [[in2]] 0
+; CHECK-NOT: DebugValue
+%35 = OpCompositeInsert %v2float %29 %26 0
+%value = OpExtInst %void %ext DebugValue %dbg_f %35 %null_expr
+%37 = OpCompositeInsert %v2float %float_0 %35 0
+%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1
+OpStore %OutColor %33
+OpReturn
+OpFunctionEnd
+)";
+
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<VectorDCE>(text, true);
+}
+
+TEST_F(VectorDCETest, RemoveDebugValueUsesKilledInstr) {
+ // It tests that the vector DCE pass removes the OpenCL.DebugInfo.100
+ // DebugValue instruction using a killed instruction.
+ const std::string text = R"(
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %In0 %OutColor
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 450
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+OpSourceExtension "GL_GOOGLE_include_directive"
+OpName %main "main"
+OpName %In0 "In0"
+OpName %OutColor "OutColor"
+OpDecorate %In0 Location 0
+OpDecorate %OutColor Location 0
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%In0 = OpVariable %_ptr_Input_v4float Input
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%_ptr_Input_float = OpTypePointer Input %float
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%v3float = OpTypeVector %float 3
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_20 = OpConstant %int 20
+%bool = OpTypeBool
+%float_1 = OpConstant %float 1
+%int_1 = OpConstant %int 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%OutColor = OpVariable %_ptr_Output_v4float Output
+%23 = OpUndef %v3float
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %6
+%24 = OpLabel
+%s0 = OpExtInst %void %ext DebugScope %dbg_main
+%25 = OpAccessChain %_ptr_Input_float %In0 %uint_0
+%26 = OpLoad %float %25
+%27 = OpAccessChain %_ptr_Input_float %In0 %uint_1
+%28 = OpLoad %float %27
+
+; CHECK: [[undef:%\w+]] = OpUndef %float
+; CHECK-NOT: DebugValue
+%value = OpExtInst %void %ext DebugValue %dbg_f %28 %null_expr
+%29 = OpAccessChain %_ptr_Input_float %In0 %uint_2
+%30 = OpLoad %float %29
+
+; CHECK: [[composite:%\w+]] = OpCompositeConstruct %v3float {{%\w+}} [[undef]] [[undef]]
+; CHECK-NEXT: DebugValue {{%\w+}} [[composite]]
+%31 = OpCompositeConstruct %v3float %30 %28 %26
+%value_live = OpExtInst %void %ext DebugValue %dbg_f %31 %null_expr
+OpBranch %32
+%32 = OpLabel
+%s1 = OpExtInst %void %ext DebugScope %dbg_main
+%33 = OpPhi %v3float %31 %24 %34 %35
+%36 = OpPhi %int %int_0 %24 %37 %35
+OpLoopMerge %38 %35 None
+OpBranch %39
+%39 = OpLabel
+%s2 = OpExtInst %void %ext DebugScope %dbg_main
+%40 = OpSLessThan %bool %36 %int_20
+OpBranchConditional %40 %41 %38
+%41 = OpLabel
+%s3 = OpExtInst %void %ext DebugScope %dbg_main
+%42 = OpCompositeExtract %float %33 0
+%43 = OpFAdd %float %42 %float_1
+%34 = OpCompositeInsert %v3float %43 %33 0
+OpBranch %35
+%35 = OpLabel
+%s4 = OpExtInst %void %ext DebugScope %dbg_main
+%37 = OpIAdd %int %36 %int_1
+OpBranch %32
+%38 = OpLabel
+%s5 = OpExtInst %void %ext DebugScope %dbg_main
+%44 = OpCompositeExtract %float %33 0
+%45 = OpCompositeConstruct %v4float %44 %44 %44 %44
+OpStore %OutColor %45
+OpReturn
+OpFunctionEnd
+)";
+
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<VectorDCE>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp b/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
index 33e52f0..e944109 100644
--- a/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/wrap_opkill_test.cpp
@@ -193,6 +193,310 @@
SinglePassRunAndMatch<WrapOpKill>(text, true);
}
+TEST_F(WrapOpKillTest, SingleOpTerminateInvocation) {
+ const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 330
+ OpName %main "main"
+ %void = OpTypeVoid
+ %5 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %main = OpFunction %void None %5
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranch %12
+ %12 = OpLabel
+ OpBranchConditional %true %13 %10
+ %13 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ %14 = OpFunctionCall %void %kill_
+ OpBranch %9
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %kill_ = OpFunction %void None %5
+ %15 = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleTerminateInvocationInSameFunc) {
+ const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK-NEXT: OpBranchConditional
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 330
+ OpName %main "main"
+ %void = OpTypeVoid
+ %5 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %main = OpFunction %void None %5
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranch %12
+ %12 = OpLabel
+ OpBranchConditional %true %13 %10
+ %13 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ %14 = OpFunctionCall %void %kill_
+ OpBranch %9
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %kill_ = OpFunction %void None %5
+ %15 = OpLabel
+ OpSelectionMerge %16 None
+ OpBranchConditional %true %17 %18
+ %17 = OpLabel
+ OpTerminateInvocation
+ %18 = OpLabel
+ OpTerminateInvocation
+ %16 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, MultipleOpTerminateInvocationDifferentFunc) {
+ const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
+; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
+; CHECK: [[orig_kill1]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[orig_kill2]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 330
+ OpName %main "main"
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %main = OpFunction %void None %4
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpLoopMerge %9 %10 None
+ OpBranch %11
+ %11 = OpLabel
+ OpBranchConditional %true %12 %9
+ %12 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ %13 = OpFunctionCall %void %14
+ %15 = OpFunctionCall %void %16
+ OpBranch %8
+ %9 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %void None %4
+ %17 = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+ %16 = OpFunction %void None %4
+ %18 = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, KillAndTerminateInvocationSameFunc) {
+ const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill:%\w+]]
+; CHECK: [[orig_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpSelectionMerge
+; CHECK-NEXT: OpBranchConditional
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+; CHECK-NEXT: [[new_terminate]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 330
+ OpName %main "main"
+ %void = OpTypeVoid
+ %5 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %main = OpFunction %void None %5
+ %8 = OpLabel
+ OpBranch %9
+ %9 = OpLabel
+ OpLoopMerge %10 %11 None
+ OpBranch %12
+ %12 = OpLabel
+ OpBranchConditional %true %13 %10
+ %13 = OpLabel
+ OpBranch %11
+ %11 = OpLabel
+ %14 = OpFunctionCall %void %kill_
+ OpBranch %9
+ %10 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %kill_ = OpFunction %void None %5
+ %15 = OpLabel
+ OpSelectionMerge %16 None
+ OpBranchConditional %true %17 %18
+ %17 = OpLabel
+ OpKill
+ %18 = OpLabel
+ OpTerminateInvocation
+ %16 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
+TEST_F(WrapOpKillTest, KillAndTerminateInvocationDifferentFunc) {
+ const std::string text = R"(
+; CHECK: OpEntryPoint Fragment [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]]
+; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]]
+; CHECK: [[orig_kill1]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[orig_kill2]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]]
+; CHECK-NEXT: OpReturn
+; CHECK: [[new_kill]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpKill
+; CHECK-NEXT: OpFunctionEnd
+; CHECK-NEXT: [[new_terminate]] = OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpTerminateInvocation
+; CHECK-NEXT: OpFunctionEnd
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 330
+ OpName %main "main"
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %bool = OpTypeBool
+ %true = OpConstantTrue %bool
+ %main = OpFunction %void None %4
+ %7 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpLoopMerge %9 %10 None
+ OpBranch %11
+ %11 = OpLabel
+ OpBranchConditional %true %12 %9
+ %12 = OpLabel
+ OpBranch %10
+ %10 = OpLabel
+ %13 = OpFunctionCall %void %14
+ %15 = OpFunctionCall %void %16
+ OpBranch %8
+ %9 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %14 = OpFunction %void None %4
+ %17 = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+ %16 = OpFunction %void None %4
+ %18 = OpLabel
+ OpKill
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<WrapOpKill>(text, true);
+}
+
TEST_F(WrapOpKillTest, FuncWithReturnValue) {
const std::string text = R"(
; CHECK: OpEntryPoint Fragment [[main:%\w+]]
diff --git a/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp b/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
index 679bee4..7ab4ca5 100644
--- a/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
+++ b/third_party/SPIRV-Tools/test/text_to_binary.constant_test.cpp
@@ -485,8 +485,15 @@
const int64_t kMaxSigned48Bit = (int64_t(1) << 47) - 1;
const int64_t kMinSigned48Bit = -kMaxSigned48Bit - 1;
+using ConstantRoundTripTest = RoundTripTest;
+
+TEST_P(ConstantRoundTripTest, DisassemblyEqualsAssemblyInput) {
+ const std::string assembly = GetParam();
+ EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly;
+}
+
INSTANTIATE_TEST_SUITE_P(
- OpConstantRoundTrip, RoundTripTest,
+ OpConstantRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
// 16 bit
"%1 = OpTypeInt 16 0\n%2 = OpConstant %1 0\n",
@@ -529,7 +536,7 @@
}));
INSTANTIATE_TEST_SUITE_P(
- OpConstantHalfRoundTrip, RoundTripTest,
+ OpConstantHalfRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x0p+0\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x0p+0\n",
@@ -566,7 +573,7 @@
// clang-format off
// (Clang-format really wants to break up these strings across lines.
INSTANTIATE_TEST_SUITE_P(
- OpConstantRoundTripNonFinite, RoundTripTest,
+ OpConstantRoundTripNonFinite, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n", // -inf
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n", // inf
@@ -596,7 +603,7 @@
// clang-format on
INSTANTIATE_TEST_SUITE_P(
- OpSpecConstantRoundTrip, RoundTripTest,
+ OpSpecConstantRoundTrip, ConstantRoundTripTest,
::testing::ValuesIn(std::vector<std::string>{
// 16 bit
"%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 0\n",
diff --git a/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp b/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
index ae51f55..3e117b8 100644
--- a/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
+++ b/third_party/SPIRV-Tools/test/text_to_binary.control_flow_test.cpp
@@ -293,8 +293,15 @@
MakeSwitchTestCase(64, 1, "0x700000123", {0x123, 7}, "12", {12, 0}),
})));
+using ControlFlowRoundTripTest = RoundTripTest;
+
+TEST_P(ControlFlowRoundTripTest, DisassemblyEqualsAssemblyInput) {
+ const std::string assembly = GetParam();
+ EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly;
+}
+
INSTANTIATE_TEST_SUITE_P(
- OpSwitchRoundTripUnsignedIntegers, RoundTripTest,
+ OpSwitchRoundTripUnsignedIntegers, ControlFlowRoundTripTest,
ValuesIn(std::vector<std::string>({
// Unsigned 16-bit.
"%1 = OpTypeInt 16 0\n%2 = OpConstant %1 65535\nOpSwitch %2 %3\n",
@@ -310,7 +317,7 @@
})));
INSTANTIATE_TEST_SUITE_P(
- OpSwitchRoundTripSignedIntegers, RoundTripTest,
+ OpSwitchRoundTripSignedIntegers, ControlFlowRoundTripTest,
ValuesIn(std::vector<std::string>{
// Signed 16-bit, with two non-default cases
"%1 = OpTypeInt 16 1\n%2 = OpConstant %1 32767\n"
@@ -381,12 +388,35 @@
}));
// clang-format on
+using OpKillTest = spvtest::TextToBinaryTest;
+
+INSTANTIATE_TEST_SUITE_P(OpKillTest, ControlFlowRoundTripTest,
+ Values("OpKill\n"));
+
+TEST_F(OpKillTest, ExtraArgsAssemblyError) {
+ const std::string input = "OpKill 1";
+ EXPECT_THAT(CompileFailure(input),
+ Eq("Expected <opcode> or <result-id> at the beginning of an "
+ "instruction, found '1'."));
+}
+
+using OpTerminateInvocationTest = spvtest::TextToBinaryTest;
+
+INSTANTIATE_TEST_SUITE_P(OpTerminateInvocationTest, ControlFlowRoundTripTest,
+ Values("OpTerminateInvocation\n"));
+
+TEST_F(OpTerminateInvocationTest, ExtraArgsAssemblyError) {
+ const std::string input = "OpTerminateInvocation 1";
+ EXPECT_THAT(CompileFailure(input),
+ Eq("Expected <opcode> or <result-id> at the beginning of an "
+ "instruction, found '1'."));
+}
+
// TODO(dneto): OpPhi
// TODO(dneto): OpLoopMerge
// TODO(dneto): OpLabel
// TODO(dneto): OpBranch
// TODO(dneto): OpSwitch
-// TODO(dneto): OpKill
// TODO(dneto): OpReturn
// TODO(dneto): OpReturnValue
// TODO(dneto): OpUnreachable
diff --git a/third_party/SPIRV-Tools/test/unit_spirv.cpp b/third_party/SPIRV-Tools/test/unit_spirv.cpp
index 0854439..33af273 100644
--- a/third_party/SPIRV-Tools/test/unit_spirv.cpp
+++ b/third_party/SPIRV-Tools/test/unit_spirv.cpp
@@ -47,10 +47,5 @@
EXPECT_THAT(s.str(), Eq("xx10 0x0000000a 0x00000010 xx11"));
}
-TEST_P(RoundTripTest, Sample) {
- EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam()))
- << GetParam();
-}
-
} // namespace
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/CMakeLists.txt b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
index 138e711..23d7a19 100644
--- a/third_party/SPIRV-Tools/test/val/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/val/CMakeLists.txt
@@ -38,6 +38,7 @@
val_entry_point.cpp
val_explicit_reserved_test.cpp
val_extensions_test.cpp
+ val_extension_spv_khr_terminate_invocation.cpp
val_ext_inst_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
diff --git a/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp b/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
index cd723b2..aca0f3c 100644
--- a/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_atomics_test.cpp
@@ -238,6 +238,120 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
+TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) {
+ const std::string body = R"(
+%val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicIAdd: "
+ "expected Result Type to be int scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType2) {
+ const std::string body = R"(
+%val1 = OpAtomicIAdd %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicIAdd: "
+ "expected Result Type to be integer scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkan) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body));
+ ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: "
+ "AtomicFloat32AddEXT AtomicFloat64AddEXT"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1
+)";
+ const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicFAddEXT: "
+ "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType2) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %u32 %u32_var %device %relaxed %u32_1
+)";
+ const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicFAddEXT: "
+ "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType3) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %u64 %u64_var %device %relaxed %u64_1
+)";
+ const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicFAddEXT: "
+ "expected Result Type to be float scalar type"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongCapability) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+ const std::string extra = R"(
+OpCapability AtomicFloat64AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("AtomicFAddEXT: float add atomics "
+ "require the AtomicFloat32AddEXT capability"));
+}
+
+TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) {
+ const std::string body = R"(
+%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1
+)";
+ const std::string extra = R"(
+OpCapability AtomicFloat32AddEXT
+OpExtension "SPV_EXT_shader_atomic_float_add"
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) {
const std::string body = R"(
%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
@@ -245,9 +359,25 @@
)";
CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("expected Result Type to be int scalar type"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) {
+ const std::string body = R"(
+OpAtomicStore %f32_var %device %relaxed %f32_1
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicExchangeFloatVulkan) {
+ const std::string body = R"(
+%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
+)";
+
+ CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}
TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) {
@@ -355,10 +485,7 @@
)";
CompileSuccessfully(GenerateShaderCode(body));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("AtomicLoad: "
- "expected Result Type to be int scalar type"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) {
@@ -733,10 +860,7 @@
)";
CompileSuccessfully(GenerateShaderCode(body));
- ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("AtomicExchange: "
- "expected Result Type to be int scalar type"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) {
diff --git a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
index 58593dc..b1ab0c9 100644
--- a/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_builtins_test.cpp
@@ -1952,7 +1952,7 @@
"needs to be a 32-bit int", "is not an int"))));
INSTANTIATE_TEST_SUITE_P(
- WhitelistRejection,
+ AllowListRejection,
ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult,
Combine(Values("PointSize", "ClipDistance", "CullDistance", "VertexId",
"InstanceId", "PointCoord", "SampleMask", "HelperInvocation",
@@ -2532,7 +2532,9 @@
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
+OpDecorate %input_type Block
OpMemberDecorate %input_type 0 BuiltIn Position
+OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn Position
)";
@@ -2543,8 +2545,7 @@
%input = OpVariable %input_ptr Input
%input_f32vec4_ptr = OpTypePointer Input %f32vec4
%output_type = OpTypeStruct %f32vec4
-%arrayed_output_type = OpTypeArray %output_type %u32_3
-%output_ptr = OpTypePointer Output %arrayed_output_type
+%output_ptr = OpTypePointer Output %output_type
%output = OpVariable %output_ptr Output
%output_f32vec4_ptr = OpTypePointer Output %f32vec4
)";
@@ -2555,7 +2556,7 @@
entry_point.interfaces = "%input %output";
entry_point.body = R"(
%input_pos = OpAccessChain %input_f32vec4_ptr %input %u32_0 %u32_0
-%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0 %u32_0
+%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0
%pos = OpLoad %f32vec4 %input_pos
OpStore %output_pos %pos
)";
@@ -2979,7 +2980,7 @@
TEST_F(ValidateBuiltIns, ValidBuiltinsForMeshShader) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.capabilities_ += R"(
-OpCapability MeshShadingNV
+OpCapability MeshShadingNV
)";
generator.extensions_ = R"(
@@ -3017,7 +3018,7 @@
TEST_F(ValidateBuiltIns, InvalidBuiltinsForMeshShader) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.capabilities_ += R"(
-OpCapability MeshShadingNV
+OpCapability MeshShadingNV
)";
generator.extensions_ = R"(
@@ -3062,6 +3063,8 @@
OpEntryPoint Fragment %4 "PSMa" %12 %17
OpExecutionMode %4 OriginUpperLeft
OpDecorate %gl_PointCoord BuiltIn PointCoord
+ OpDecorate %12 Location 0
+ OpDecorate %17 Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
diff --git a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
index 8580818..756f762 100644
--- a/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_capability_test.cpp
@@ -1367,8 +1367,9 @@
std::vector<std::string>{"GeometryStreams"}),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
- "OpDecorate %intt Location 0\n"
- "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
+ "OpMemberDecorate %struct 0 Location 0\n"
+ "%intt = OpTypeInt 32 0\n"
+ "%struct = OpTypeStruct %intt\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
index 0d09642..630a19d 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -1204,14 +1204,6 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
-TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) {
- CompileSuccessfully(
- GetUnreachableMergeWithBranchUse(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
- ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("cannot be the target of a branch."));
-}
-
std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap,
spv_target_env env) {
std::string header =
@@ -4503,6 +4495,76 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpBranch %5
+%5 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+
+ auto f = vstate_->function(3);
+ auto entry = f->GetBlock(4).first;
+ ASSERT_TRUE(entry->reachable());
+ auto end = f->GetBlock(5).first;
+ ASSERT_TRUE(end->reachable());
+}
+
+TEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpTypeBool
+%4 = OpUndef %3
+%5 = OpFunction %1 None %2
+%6 = OpLabel
+OpBranch %7
+%7 = OpLabel
+OpSelectionMerge %8 None
+OpBranchConditional %4 %9 %10
+%8 = OpLabel
+OpReturn
+%9 = OpLabel
+OpBranch %8
+%10 = OpLabel
+OpBranch %8
+%11 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+
+ auto f = vstate_->function(5);
+ auto b6 = f->GetBlock(6).first;
+ auto b7 = f->GetBlock(7).first;
+ auto b8 = f->GetBlock(8).first;
+ auto b9 = f->GetBlock(9).first;
+ auto b10 = f->GetBlock(10).first;
+ auto b11 = f->GetBlock(11).first;
+
+ ASSERT_TRUE(b6->reachable());
+ ASSERT_TRUE(b7->reachable());
+ ASSERT_TRUE(b8->reachable());
+ ASSERT_TRUE(b9->reachable());
+ ASSERT_TRUE(b10->reachable());
+ ASSERT_FALSE(b11->reachable());
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
index 204f468..e646162 100644
--- a/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_decoration_test.cpp
@@ -6353,7 +6353,7 @@
"requires one of these capabilities"))));
INSTANTIATE_TEST_SUITE_P(
- DecorationWhitelistFailure, ValidateWebGPUCombineDecorationResult,
+ DecorationAllowListFailure, ValidateWebGPUCombineDecorationResult,
Combine(Values("RelaxedPrecision", "BufferBlock", "GLSLShared",
"GLSLPacked", "Invariant", "Volatile", "Coherent"),
Values(TestResult(
@@ -7097,6 +7097,68 @@
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}
+TEST_F(ValidateDecorations, LocationVariableGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %in_var Location 0
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%in_var = OpVariable %ptr_input_float Input
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDecorations, LocationStructMemberGood) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 0 Location 0
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDecorations, LocationStructBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct Location 0
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Location decoration can only be applied to a variable "
+ "or member of a structure type"));
+}
+
+TEST_F(ValidateDecorations, LocationFloatBad) {
+ const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %float Location 0
+%float = OpTypeFloat 32
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Location decoration can only be applied to a variable "
+ "or member of a structure type"));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp b/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp
new file mode 100644
index 0000000..4cabf9e
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/val/val_extension_spv_khr_terminate_invocation.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/enum_string_mapping.h"
+#include "source/extensions.h"
+#include "source/spirv_target_env.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRTerminateInvocation = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRTerminateInvocation, Valid) {
+ const std::string str = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) {
+ const std::string str = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("TerminateInvocation requires one of the following "
+ "extensions: SPV_KHR_terminate_invocation"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) {
+ const std::string str = R"(
+ OpCapability Kernel
+ OpCapability Addresses
+ OpExtension "SPV_KHR_terminate_invocation"
+ OpMemoryModel Physical32 OpenCL
+ OpEntryPoint Kernel %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "TerminateInvocation requires one of these capabilities: Shader \n"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, RequiresFragmentShader) {
+ const std::string str = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("OpTerminateInvocation requires Fragment execution model"));
+}
+
+TEST_F(ValidateSpvKHRTerminateInvocation, IsTerminatorInstruction) {
+ const std::string str = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_terminate_invocation"
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %main "main"
+
+ %void = OpTypeVoid
+ %void_fn = OpTypeFunction %void
+
+ %main = OpFunction %void None %void_fn
+ %entry = OpLabel
+ OpTerminateInvocation
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(str.c_str());
+ EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Return must appear in a block"));
+}
+
+} // namespace
+} // namespace val
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp b/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
index 682c321..491a808 100644
--- a/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_extensions_test.cpp
@@ -62,7 +62,8 @@
"SPV_EXT_shader_viewport_index_layer",
"SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
"SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
- "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing"));
+ "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing",
+ "SPV_KHR_terminate_invocation"));
INSTANTIATE_TEST_SUITE_P(FailSilently, ValidateUnknownExtensions,
Values("ERROR_unknown_extension", "SPV_KHR_",
diff --git a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
index 3410616..f2fb45a 100644
--- a/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_interfaces_test.cpp
@@ -399,6 +399,986 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
}
+TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentVariable) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %var Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%var = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Variable has conflicting location decorations"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableAndMemberAssigned) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Members cannot be assigned a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMemberAndSubMemberAssigned) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %outer Block
+OpMemberDecorate %outer 0 Location 0
+OpMemberDecorate %struct 0 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%outer = OpTypeStruct %struct
+%ptr_input_outer = OpTypePointer Input %outer
+%var = OpVariable %ptr_input_outer Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Members cannot be assigned a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentStructMember) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Member index 1 has conflicting location assignments"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentStructMember) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 1 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Member index 0 is missing a location assignment"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentNonBlockStruct) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Variable must be decorated with a location"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictInput) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 0"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutput) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting output location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+ VulkanLocationsSameLocationInputAndOutputNoConflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableInGap) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 2
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%ptr_input_float = OpTypePointer Input %float
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeFloatVectorConflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_vector = OpTypePointer Input %vector
+%var1 = OpVariable %ptr_input_vector Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeIntVectorConflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Int64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%long = OpTypeInt 64 0
+%vector = OpTypeVector %long 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_vector = OpTypePointer Input %vector
+%var1 = OpVariable %ptr_input_vector Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix2x2Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 2
+%matrix = OpTypeMatrix %vector 2
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix3x3Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 2
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 3
+%matrix = OpTypeMatrix %vector 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 2"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 3
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%vector = OpTypeVector %float 4
+%matrix = OpTypeMatrix %vector 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 3"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix2x2Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 2
+%matrix = OpTypeMatrix %vector 2
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix3x3Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 5
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 3
+%matrix = OpTypeMatrix %vector 3
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 5"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix4x4Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 7
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%vector = OpTypeVector %double 4
+%matrix = OpTypeMatrix %vector 4
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_matrix = OpTypePointer Input %matrix
+%var1 = OpVariable %ptr_input_matrix Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 7"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArray2Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%array = OpTypeArray %int %int_2
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArray4Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 3
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 3"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Array4Conflict) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 0
+OpDecorate %var2 Location 15
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%vector = OpTypeVector %float 4
+%matrix = OpTypeMatrix %vector 4
+%array = OpTypeArray %matrix %int_4
+%struct = OpTypeStruct %array
+%ptr_input_float = OpTypePointer Input %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%var2 = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 15"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentDisambiguates) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 1 Location 0
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentIn64BitVec3) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 1
+OpMemberDecorate %struct 1 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%double3 = OpTypeVector %double 3
+%struct = OpTypeStruct %double3 %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting input location assignment "
+ "at location 1, component 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsComponentAfter64BitVec3) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Float64
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 1 Location 1
+OpMemberDecorate %struct 1 Component 2
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%double3 = OpTypeVector %double 3
+%struct = OpTypeStruct %double3 %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsConflictingComponentVariable) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var Location 0
+OpDecorate %var Component 0
+OpDecorate %var Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_input_float = OpTypePointer Input %float
+%var = OpVariable %ptr_input_float Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Variable has conflicting component decorations"));
+}
+
+TEST_F(ValidateInterfacesTest,
+ VulkanLocationsConflictingComponentStructMember) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Location 0
+OpMemberDecorate %struct 0 Component 0
+OpMemberDecorate %struct 0 Component 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Member index 0 has conflicting component assignments"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutputIndex1) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+OpDecorate %var2 Location 1
+OpDecorate %var2 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting output location assignment "
+ "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+ VulkanLocationsVariableNoConflictDifferentIndex) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1 %var2
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 0
+OpDecorate %var2 Location 1
+OpDecorate %var2 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%var2 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsIndexGLCompute) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var1
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_output_struct = OpTypePointer Output %struct
+%var1 = OpVariable %ptr_output_struct Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Index can only be applied to Fragment output variables"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsIndexInput) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %var1
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %var1 Location 1
+OpDecorate %var1 Index 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%struct = OpTypeStruct %float %float
+%ptr_input_struct = OpTypePointer Input %struct
+%var1 = OpVariable %ptr_input_struct Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Index can only be applied to Fragment output variables"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %11 Location 0
+OpDecorate %18 Component 0
+OpDecorate %18 Location 0
+OpDecorate %28 Component 1
+OpDecorate %28 Location 0
+OpDecorate %36 Location 1
+OpDecorate %40 Component 0
+OpDecorate %40 Location 1
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%11 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%18 = OpVariable %_ptr_Output_float Output
+%uint = OpTypeInt 32 0
+%v3float = OpTypeVector %float 3
+%uint_2 = OpConstant %uint 2
+%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
+%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2
+%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%36 = OpVariable %_ptr_Input_v4float Input
+%40 = OpVariable %_ptr_Output_float Output
+%4 = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponentBad) {
+ const std::string text = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40
+OpExecutionMode %4 OriginUpperLeft
+OpDecorate %11 Location 0
+OpDecorate %18 Component 0
+OpDecorate %18 Location 0
+OpDecorate %28 Component 1
+OpDecorate %28 Location 0
+OpDecorate %36 Location 1
+OpDecorate %40 Component 1
+OpDecorate %40 Location 1
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%11 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_float = OpTypePointer Output %float
+%18 = OpVariable %_ptr_Output_float Output
+%uint = OpTypeInt 32 0
+%v3float = OpTypeVector %float 3
+%uint_2 = OpConstant %uint 2
+%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
+%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2
+%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%36 = OpVariable %_ptr_Input_v4float Input
+%40 = OpVariable %_ptr_Output_float Output
+%4 = OpFunction %void None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Entry-point has conflicting output location "
+ "assignment at location 1, component 1"));
+}
+
+TEST_F(ValidateInterfacesTest, VulkanLocationsLargeLocation) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "????????" %17
+ OpExecutionMode %4 OriginUpperLeft
+ OpDecorate %17 Location 4227868160
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+ %17 = OpVariable %_ptr_Input_v3float Input
+ %4 = OpFunction %void None %3
+ %5 = OpLabel
+ OpUnreachable
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
index 688f433..0a1476e 100644
--- a/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_modes_test.cpp
@@ -714,14 +714,14 @@
"SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"),
Values(SPV_ENV_UNIVERSAL_1_3)));
-INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUWhitelistGood,
+INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUAllowListGood,
ValidateModeExecution,
Combine(Values(SPV_SUCCESS), Values(""),
Values("GLCompute"), Values("LocalSize 1 1 1"),
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
- ValidateModeGLComputeWebGPUWhitelistBad, ValidateModeExecution,
+ ValidateModeGLComputeWebGPUAllowListBad, ValidateModeExecution,
Combine(Values(SPV_ERROR_INVALID_DATA),
Values("Execution mode must be one of OriginUpperLeft, "
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
@@ -730,14 +730,14 @@
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
- ValidateModeFragmentWebGPUWhitelistGood, ValidateModeExecution,
+ ValidateModeFragmentWebGPUAllowListGood, ValidateModeExecution,
Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"),
Values("OriginUpperLeft", "DepthReplacing", "DepthGreater",
"DepthLess", "DepthUnchanged"),
Values(SPV_ENV_WEBGPU_0)));
INSTANTIATE_TEST_SUITE_P(
- ValidateModeFragmentWebGPUWhitelistBad, ValidateModeExecution,
+ ValidateModeFragmentWebGPUAllowListBad, ValidateModeExecution,
Combine(Values(SPV_ERROR_INVALID_DATA),
Values("Execution mode must be one of OriginUpperLeft, "
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
diff --git a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
index e81fc7c..62fa6a7 100644
--- a/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_webgpu_test.cpp
@@ -239,22 +239,73 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
+TEST_F(ValidateWebGPU, LogicalAddressingGLSL450MemoryGood) {
std::string spirv = R"(
- OpCapability Shader
- OpMemoryModel Logical GLSL450
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %func "shader"
+%void = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func = OpFunction %void None %void_f
+%label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, LogicalAddressingSimpleMemoryGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint Vertex %func "shader"
+%void = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func = OpFunction %void None %void_f
+%label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, KernelIsBad) {
+ std::string spirv = R"(
+ OpCapability Kernel
+ OpMemoryModel Logical Simple
OpNoLine
)";
CompileSuccessfully(spirv);
- EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+ ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Memory model must be VulkanKHR for WebGPU "
- "environment.\n OpMemoryModel Logical GLSL450\n"));
+ HasSubstr("Capability Kernel is not allowed by WebGPU "
+ "specification (or requires extension)"));
}
-TEST_F(ValidateWebGPU, WhitelistedExtendedInstructionsImportGood) {
+TEST_F(ValidateWebGPU, OpenCLMemoryModelBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical OpenCL
+ OpNoLine
+)";
+
+ CompileSuccessfully(spirv);
+
+ EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+ ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Operand 2 of MemoryModel requires one of these "
+ "capabilities: Kernel"));
+}
+
+TEST_F(ValidateWebGPU, AllowListedExtendedInstructionsImportGood) {
std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
@@ -275,7 +326,7 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
-TEST_F(ValidateWebGPU, NonWhitelistedExtendedInstructionsImportBad) {
+TEST_F(ValidateWebGPU, NonAllowListedExtendedInstructionsImportBad) {
std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
diff --git a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
index 2c9807d..61064d1 100644
--- a/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
+++ b/third_party/SPIRV-Tools/tools/fuzz/fuzz.cpp
@@ -121,6 +121,18 @@
--replay
File from which to read a sequence of transformations to replay
(instead of fuzzing)
+ --replay-range=
+ Signed 32-bit integer. If set to a positive value N, only the
+ first N transformations will be applied during replay. If set to
+ a negative value -N, all but the final N transformations will be
+ applied during replay. If set to 0 (the default), all
+ transformations will be applied during replay. Ignored unless
+ --replay is used.
+ --replay-validation
+ Run the validator after applying each transformation during
+ replay (including the replay that occurs during shrinking).
+ Aborts if an invalid binary is created. Useful for debugging
+ spirv-fuzz.
--seed=
Unsigned 32-bit integer seed to control random number
generation.
@@ -137,11 +149,6 @@
extension will be added. The default is "temp_", which will
cause files like "temp_0001.spv" to be output to the current
directory. Ignored unless --shrink is used.
- --replay-validation
- Run the validator after applying each transformation during
- replay (including the replay that occurs during shrinking).
- Aborts if an invalid binary is created. Useful for debugging
- spirv-fuzz.
--version
Display fuzzer version information.
@@ -208,6 +215,15 @@
} else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) {
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
*replay_transformations_file = std::string(split_flag.second);
+ } else if (0 == strncmp(cur_arg, "--replay-range=",
+ sizeof("--replay-range=") - 1)) {
+ const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
+ char* end = nullptr;
+ errno = 0;
+ const auto replay_range =
+ static_cast<int32_t>(strtol(split_flag.second.c_str(), &end, 10));
+ assert(end != split_flag.second.c_str() && errno == 0);
+ fuzzer_options->set_replay_range(replay_range);
} else if (0 == strncmp(cur_arg, "--replay-validation",
sizeof("--replay-validation") - 1)) {
fuzzer_options->enable_replay_validation();
@@ -392,9 +408,27 @@
spvtools::fuzz::Replayer replayer(
target_env, fuzzer_options->replay_validation_enabled, validator_options);
replayer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
- auto replay_result_status =
- replayer.Run(binary_in, initial_facts, transformation_sequence,
- binary_out, transformations_applied);
+
+ uint32_t num_transformations_to_apply;
+ if (fuzzer_options->replay_range > 0) {
+ // We have a positive replay range, N. We would like transformations
+ // [0, N), truncated to the number of available transformations if N is too
+ // large.
+ num_transformations_to_apply = static_cast<uint32_t>(
+ std::min(fuzzer_options->replay_range,
+ transformation_sequence.transformation_size()));
+ } else {
+ // We have non-positive replay range, -N (where N may be 0). We would like
+ // transformations [0, num_transformations - N), or no transformations if N
+ // is too large.
+ num_transformations_to_apply = static_cast<uint32_t>(
+ std::max(0, transformation_sequence.transformation_size() +
+ fuzzer_options->replay_range));
+ }
+
+ auto replay_result_status = replayer.Run(
+ binary_in, initial_facts, transformation_sequence,
+ num_transformations_to_apply, binary_out, transformations_applied);
return !(replay_result_status !=
spvtools::fuzz::Replayer::ReplayerResultStatus::kComplete);
}
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py
index 4467a32..39d27cb 100755
--- a/third_party/SPIRV-Tools/utils/check_copyright.py
+++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -35,7 +35,8 @@
'Samsung Inc',
'André Perez Maselco',
'Vasyl Teliman',
- 'Advanced Micro Devices, Inc.']
+ 'Advanced Micro Devices, Inc.',
+ 'Stefano Milizia']
CURRENT_YEAR='2020'
YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
diff --git a/third_party/SPIRV-Tools/utils/check_symbol_exports.py b/third_party/SPIRV-Tools/utils/check_symbol_exports.py
index e14c2eb..bcd77da 100755
--- a/third_party/SPIRV-Tools/utils/check_symbol_exports.py
+++ b/third_party/SPIRV-Tools/utils/check_symbol_exports.py
@@ -55,11 +55,11 @@
# _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_]
symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
- # In addition, the following pattern whitelists global functions that are added
+ # In addition, the following pattern allowlists global functions that are added
# by the protobuf compiler:
# - AddDescriptors_spvtoolsfuzz_2eproto()
# - InitDefaults_spvtoolsfuzz_2eproto()
- symbol_whitelist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
+ symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
seen = set()
result = 0
@@ -70,7 +70,7 @@
if symbol not in seen:
seen.add(symbol)
#print("look at '{}'".format(symbol))
- if not (symbol_whitelist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
+ if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
result = 1
return result
diff --git a/third_party/SPIRV-Tools/utils/roll_deps.sh b/third_party/SPIRV-Tools/utils/roll_deps.sh
index 622afc9..7ecfdd3 100755
--- a/third_party/SPIRV-Tools/utils/roll_deps.sh
+++ b/third_party/SPIRV-Tools/utils/roll_deps.sh
@@ -13,19 +13,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Attempts to roll all entries in DEPS to origin/master and creates a
-# commit.
+# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
#
# Depends on roll-dep from depot_path being in PATH.
+effcee_dir="external/effcee/"
+effcee_trunk="origin/main"
+googletest_dir="external/googletest/"
+googletest_trunk="origin/master"
+re2_dir="external/re2/"
+re2_trunk="origin/master"
+spirv_headers_dir="external/spirv-headers/"
+spirv_headers_trunk="origin/master"
+
# This script assumes it's parent directory is the repo root.
repo_path=$(dirname "$0")/..
-effcee_dir="external/effcee/"
-googletest_dir="external/googletest/"
-re2_dir="external/re2/"
-spirv_headers_dir="external/spirv-headers/"
-
cd "$repo_path"
-roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"
+if [[ $(git diff --stat) != '' ]]; then
+ echo "Working tree is dirty, commit changes before attempting to roll DEPS"
+ exit 1
+fi
+
+old_head=$(git rev-parse HEAD)
+
+roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
+
+git rebase --interactive "${old_head}"